Compare commits
120 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 060cab76d5 | |||
| afb8a35eac | |||
| 1c0dd71020 | |||
| 1633668d7f | |||
| 6756f792c8 | |||
| bae4db8a7f | |||
| c47fd2cfce | |||
| cb08bb7196 | |||
| f3500623ff | |||
| 4c4cbed43b | |||
| 23fb5689b7 | |||
| b551466f94 | |||
| 0a7af69d69 | |||
| 2cf42a4763 | |||
| 6f0d1ee9e7 | |||
| f82dd229d9 | |||
| 3b833f6f63 | |||
| 2517fd5c90 | |||
| 4e572679dd | |||
| de9c664d1e | |||
| 8187c5362a | |||
| a8eb260bbd | |||
| bb944f595f | |||
| 598626238b | |||
| a451f8dca4 | |||
| 4fd070c332 | |||
| 091413dd69 | |||
| ff74425132 | |||
| 6f4d1dcc57 | |||
| 9713e33fed | |||
| ab4fd17520 | |||
| 6045f1e0e3 | |||
| 102d7370bd | |||
| e22ba228a9 | |||
| 52105fcdcc | |||
| 8a1242fdbd | |||
| 5f78ab8e25 | |||
| 0899df94eb | |||
| 327e0a207c | |||
| 8d044fe3d1 | |||
| 642d95b5d1 | |||
| 6092da6fe7 | |||
| 4773da32ab | |||
| 642e42e01e | |||
| 0820ede014 | |||
| fb49aaa2d6 | |||
| c67b3082cb | |||
| 0784cc7226 | |||
| 2ce1bddce9 | |||
| 48d2cd1461 | |||
| bdee49f815 | |||
| aae7734bc0 | |||
| 054c02bd41 | |||
| 4798136074 | |||
| 389a5bc5e7 | |||
| fea26b5751 | |||
| fd7a380edd | |||
| ff021c1319 | |||
| ae38eab00d | |||
| 61f4581dc4 | |||
| 11c1381f6c | |||
| 8d37e4c8e4 | |||
| a83073f3ee | |||
| e47d7525c5 | |||
| 530a1f8a4b | |||
| 9e93dd08aa | |||
| d1aa604fe5 | |||
| 2c4ca358f3 | |||
| e23f6f1d33 | |||
| 8c7d5d47b1 | |||
| 4c743ee5b9 | |||
| 085dd9ad95 | |||
| 63172b8a69 | |||
| ccfade12b0 | |||
| cf7fc61f7b | |||
| c8c3cb7909 | |||
| ef01211639 | |||
| 4816902f9c | |||
| eec55e273e | |||
| f5b4722b6b | |||
| a9d57936c3 | |||
| 45015cd7ee | |||
| 216fc89fd2 | |||
| 7fd9986c6e | |||
| 305f207731 | |||
| ed666823a4 | |||
| 8dd2f47b7c | |||
| 1e4dcdff7c | |||
| 802c91f324 | |||
| 18ae3e194a | |||
| e3003f3268 | |||
| 03567d4281 | |||
| bf4948239e | |||
| 57ba74e543 | |||
| 6567d17f23 | |||
| 0d1c57a874 | |||
| f1aa75a94f | |||
| 40ea40a763 | |||
| 7ea0a43162 | |||
| 6a3042c437 | |||
| d22b12874d | |||
| 9a6172b4fe | |||
| 159489c7da | |||
| 24b0f09323 | |||
| 00d6ff90ea | |||
| 8bd2e8aacf | |||
| 6bf091be68 | |||
| 3b8ccff089 | |||
| cd428b28a7 | |||
| d7b7e1f7dd | |||
| 2194096ad8 | |||
| 42803ebd55 | |||
| 92433b79aa | |||
| 198ad0e02b | |||
| 4c206f7de5 | |||
| 287aa45b98 | |||
| 14a7ad03bd | |||
| e577a80811 | |||
| efbc0d97f2 | |||
| 7f7364054a |
-110
@@ -1,110 +0,0 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
import java.net.*;
|
||||
import java.io.*;
|
||||
import java.nio.channels.*;
|
||||
import java.util.Properties;
|
||||
|
||||
public class MavenWrapperDownloader {
|
||||
|
||||
/**
|
||||
* Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided.
|
||||
*/
|
||||
private static final String DEFAULT_DOWNLOAD_URL =
|
||||
"https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar";
|
||||
|
||||
/**
|
||||
* Path to the maven-wrapper.properties file, which might contain a downloadUrl property to
|
||||
* use instead of the default one.
|
||||
*/
|
||||
private static final String MAVEN_WRAPPER_PROPERTIES_PATH =
|
||||
".mvn/wrapper/maven-wrapper.properties";
|
||||
|
||||
/**
|
||||
* Path where the maven-wrapper.jar will be saved to.
|
||||
*/
|
||||
private static final String MAVEN_WRAPPER_JAR_PATH =
|
||||
".mvn/wrapper/maven-wrapper.jar";
|
||||
|
||||
/**
|
||||
* Name of the property which should be used to override the default download url for the wrapper.
|
||||
*/
|
||||
private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl";
|
||||
|
||||
public static void main(String args[]) {
|
||||
System.out.println("- Downloader started");
|
||||
File baseDirectory = new File(args[0]);
|
||||
System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath());
|
||||
|
||||
// If the maven-wrapper.properties exists, read it and check if it contains a custom
|
||||
// wrapperUrl parameter.
|
||||
File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH);
|
||||
String url = DEFAULT_DOWNLOAD_URL;
|
||||
if(mavenWrapperPropertyFile.exists()) {
|
||||
FileInputStream mavenWrapperPropertyFileInputStream = null;
|
||||
try {
|
||||
mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile);
|
||||
Properties mavenWrapperProperties = new Properties();
|
||||
mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream);
|
||||
url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url);
|
||||
} catch (IOException e) {
|
||||
System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'");
|
||||
} finally {
|
||||
try {
|
||||
if(mavenWrapperPropertyFileInputStream != null) {
|
||||
mavenWrapperPropertyFileInputStream.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
// Ignore ...
|
||||
}
|
||||
}
|
||||
}
|
||||
System.out.println("- Downloading from: : " + url);
|
||||
|
||||
File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH);
|
||||
if(!outputFile.getParentFile().exists()) {
|
||||
if(!outputFile.getParentFile().mkdirs()) {
|
||||
System.out.println(
|
||||
"- ERROR creating output direcrory '" + outputFile.getParentFile().getAbsolutePath() + "'");
|
||||
}
|
||||
}
|
||||
System.out.println("- Downloading to: " + outputFile.getAbsolutePath());
|
||||
try {
|
||||
downloadFileFromURL(url, outputFile);
|
||||
System.out.println("Done");
|
||||
System.exit(0);
|
||||
} catch (Throwable e) {
|
||||
System.out.println("- Error downloading");
|
||||
e.printStackTrace();
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
private static void downloadFileFromURL(String urlString, File destination) throws Exception {
|
||||
URL website = new URL(urlString);
|
||||
ReadableByteChannel rbc;
|
||||
rbc = Channels.newChannel(website.openStream());
|
||||
FileOutputStream fos = new FileOutputStream(destination);
|
||||
fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
|
||||
fos.close();
|
||||
rbc.close();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
= 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.
|
||||
Vendored
+114
@@ -0,0 +1,114 @@
|
||||
pipeline {
|
||||
agent none
|
||||
|
||||
triggers {
|
||||
pollSCM 'H/10 * * * *'
|
||||
upstream(upstreamProjects: "spring-data-commons/2.2.x", threshold: hudson.model.Result.SUCCESS)
|
||||
}
|
||||
|
||||
options {
|
||||
disableConcurrentBuilds()
|
||||
buildDiscarder(logRotator(numToKeepStr: '14'))
|
||||
}
|
||||
|
||||
stages {
|
||||
stage("Test") {
|
||||
when {
|
||||
anyOf {
|
||||
branch '3.2.x'
|
||||
not { triggeredBy 'UpstreamCause' }
|
||||
}
|
||||
}
|
||||
parallel {
|
||||
stage("test: baseline") {
|
||||
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('Release to artifactory') {
|
||||
when {
|
||||
anyOf {
|
||||
branch '3.2.x'
|
||||
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 '3.2.x'
|
||||
}
|
||||
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>")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+212
@@ -0,0 +1,212 @@
|
||||
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.
|
||||
* `ElasticsearchTemplate` 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 but extensible to support other metadata formats
|
||||
* Automatic implementation of `Repository` interfaces including support for custom finder 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 Node Client
|
||||
|
||||
[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: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
|
||||
|
||||
[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: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>
|
||||
----
|
||||
|
||||
=== 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 your first non-trivial change._
|
||||
|
||||
=== 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].
|
||||
@@ -1,324 +0,0 @@
|
||||
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](https://www.elastic.co/) search engine.
|
||||
|
||||
Guide
|
||||
------------
|
||||
|
||||
* [Reference Documentation](https://docs.spring.io/spring-data/elasticsearch/docs/current/reference/html/)
|
||||
* [API Documentation](https://docs.spring.io/spring-data/elasticsearch/docs/current/api/)
|
||||
* [Spring Data Project](https://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](https://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>https://repo.spring.io/libs-snapshot</url>
|
||||
</repository>
|
||||
```
|
||||
|
||||
| spring data elasticsearch | elasticsearch |
|
||||
|:-------------------------:|:-------------:|
|
||||
| 3.2.x | 6.5.0 |
|
||||
| 3.1.x | 6.2.2 |
|
||||
| 3.0.x | 5.5.0 |
|
||||
| 2.1.x | 2.4.0 |
|
||||
| 2.0.x | 2.2.0 |
|
||||
| 1.3.x | 1.5.2 |
|
||||
|
||||
|
||||
### 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);
|
||||
```
|
||||
|
||||
### Reactive Elasticsearch
|
||||
|
||||
The `ReactiveElasticsearchClient` is a non official driver based on `WebClient`.
|
||||
It uses the request/response objects provided by the Elasticsearch core project.
|
||||
|
||||
```java
|
||||
@Configuration
|
||||
public class Config {
|
||||
|
||||
@Bean
|
||||
ReactiveElasticsearchClient client() {
|
||||
|
||||
ClientConfiguration clientConfiguration = ClientConfiguration.builder()
|
||||
.connectedTo("localhost:9200", "localhost:9291")
|
||||
.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)
|
||||
);
|
||||
```
|
||||
|
||||
The reactive client response, especially for search operations, is bound to the `from` (offset) & `size` (limit) options of the request.
|
||||
|
||||
`ReactiveElasticsearchOperations` is the gateway to executing high level commands against an Elasticsearch cluster using the `ReactiveElasticsearchClient`.
|
||||
The easiest way of setting up the `ReactiveElasticsearchTemplate` is via `AbstractReactiveElasticsearchConfiguration`.
|
||||
|
||||
```java
|
||||
@Configuration
|
||||
public class Config extends AbstractReactiveElasticsearchConfiguration {
|
||||
|
||||
@Bean
|
||||
@Override
|
||||
public ReactiveElasticsearchClient reactiveElasticsearchClient() {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
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()`.
|
||||
|
||||
```java
|
||||
template.save(new Person("Bruce Banner", 42))
|
||||
.doOnNext(System.out::println)
|
||||
.flatMap(person -> template.findById(person.id, Person.class))
|
||||
.doOnNext(System.out::println)
|
||||
.flatMap(person -> template.delete(person))
|
||||
.doOnNext(System.out::println)
|
||||
.flatMap(id -> template.count(Person.class))
|
||||
.doOnNext(System.out::println)
|
||||
.subscribe();
|
||||
```
|
||||
|
||||
The above outputs the following sequence on the console.
|
||||
|
||||
```bash
|
||||
> Person(id=QjWCWWcBXiLAnp77ksfR, name=Bruce Banner, age=42)
|
||||
> Person(id=QjWCWWcBXiLAnp77ksfR, name=Bruce Banner, age=42)
|
||||
> QjWCWWcBXiLAnp77ksfR
|
||||
> 0
|
||||
```
|
||||
|
||||
### 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 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: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 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: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](https://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](https://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)
|
||||
@@ -0,0 +1,9 @@
|
||||
# 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.
|
||||
-15
@@ -1,15 +0,0 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
[[ -d $PWD/maven && ! -d $HOME/.m2 ]] && ln -s $PWD/maven $HOME/.m2
|
||||
|
||||
spring_data_elasticsearch_artifactory=$(pwd)/spring-data-elasticsearch-artifactory
|
||||
|
||||
rm -rf $HOME/.m2/repository/org/springframework/data 2> /dev/null || :
|
||||
|
||||
cd spring-data-elasticsearch-github
|
||||
|
||||
./mvnw deploy \
|
||||
-Dmaven.test.skip=true \
|
||||
-DaltDeploymentRepository=distribution::default::file://${spring_data_elasticsearch_artifactory}
|
||||
@@ -1,20 +0,0 @@
|
||||
---
|
||||
platform: linux
|
||||
|
||||
image_resource:
|
||||
type: docker-image
|
||||
source:
|
||||
repository: openjdk
|
||||
tag: 8-jdk-alpine
|
||||
|
||||
inputs:
|
||||
- name: spring-data-elasticsearch-github
|
||||
|
||||
outputs:
|
||||
- name: spring-data-elasticsearch-artifactory
|
||||
|
||||
caches:
|
||||
- path: maven
|
||||
|
||||
run:
|
||||
path: spring-data-elasticsearch-github/ci/build.sh
|
||||
-11
@@ -1,11 +0,0 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
[[ -d $PWD/maven && ! -d $HOME/.m2 ]] && ln -s $PWD/maven $HOME/.m2
|
||||
|
||||
rm -rf $HOME/.m2/repository/org/springframework/data 2> /dev/null || :
|
||||
|
||||
cd spring-data-elasticsearch-github
|
||||
|
||||
./mvnw clean dependency:list test -P${PROFILE} -Dsort
|
||||
-20
@@ -1,20 +0,0 @@
|
||||
---
|
||||
platform: linux
|
||||
|
||||
image_resource:
|
||||
type: docker-image
|
||||
source:
|
||||
repository: openjdk
|
||||
tag: 8-jdk-alpine
|
||||
|
||||
inputs:
|
||||
- name: spring-data-elasticsearch-github
|
||||
|
||||
outputs:
|
||||
- name: spring-data-elasticsearch-artifactory
|
||||
|
||||
caches:
|
||||
- path: maven
|
||||
|
||||
run:
|
||||
path: spring-data-elasticsearch-github/ci/test.sh
|
||||
@@ -5,12 +5,12 @@
|
||||
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-elasticsearch</artifactId>
|
||||
<version>3.2.0.M4</version>
|
||||
<version>3.2.3.RELEASE</version>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.data.build</groupId>
|
||||
<artifactId>spring-data-parent</artifactId>
|
||||
<version>2.2.0.M4</version>
|
||||
<version>2.2.3.RELEASE</version>
|
||||
</parent>
|
||||
|
||||
<name>Spring Data Elasticsearch</name>
|
||||
@@ -19,9 +19,10 @@
|
||||
|
||||
<properties>
|
||||
<commonslang>2.6</commonslang>
|
||||
<elasticsearch>6.7.2</elasticsearch>
|
||||
<elasticsearch>6.8.4</elasticsearch>
|
||||
<log4j>2.9.1</log4j>
|
||||
<springdata.commons>2.2.0.M4</springdata.commons>
|
||||
<springdata.commons>2.2.3.RELEASE</springdata.commons>
|
||||
<netty>4.1.39.Final</netty>
|
||||
<java-module-name>spring.data.elasticsearch</java-module-name>
|
||||
</properties>
|
||||
|
||||
@@ -72,6 +73,19 @@
|
||||
<url>https://jira.spring.io/browse/DATAES</url>
|
||||
</issueManagement>
|
||||
|
||||
<dependencyManagement>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-bom</artifactId>
|
||||
<version>${netty}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!-- Spring -->
|
||||
@@ -234,6 +248,24 @@
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<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>
|
||||
@@ -244,8 +276,8 @@
|
||||
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>servlet-api</artifactId>
|
||||
<version>3.0-alpha-1</version>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
<version>3.1.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
@@ -253,18 +285,6 @@
|
||||
|
||||
<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>
|
||||
<!--
|
||||
please do not remove this configuration for surefire - we need that to avoid issue with jar hell
|
||||
-->
|
||||
@@ -282,28 +302,53 @@
|
||||
</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>release</id>
|
||||
|
||||
<id>ci</id>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.jfrog.buildinfo</groupId>
|
||||
<artifactId>artifactory-maven-plugin</artifactId>
|
||||
<inherited>false</inherited>
|
||||
<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>
|
||||
<id>spring-libs-release</id>
|
||||
<url>https://repo.spring.io/libs-release</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
|
||||
@@ -1,15 +1,7 @@
|
||||
= Spring Data Elasticsearch - Reference Documentation
|
||||
BioMed Central Development Team; Oliver Drotbohm; Greg Turnquist; Christoph Strobl;
|
||||
BioMed Central Development Team; Oliver Drotbohm; Greg Turnquist; Christoph Strobl; Peter-Josef Meisch
|
||||
:revnumber: {version}
|
||||
:revdate: {localdate}
|
||||
:toc:
|
||||
:toc-placement!:
|
||||
:linkcss:
|
||||
:doctype: book
|
||||
:docinfo: shared
|
||||
:source-highlighter: prettify
|
||||
:icons: font
|
||||
:imagesdir: images
|
||||
ifdef::backend-epub3[:front-cover-image: image:epub-cover.png[Front Cover,1050,1600]]
|
||||
:spring-data-commons-docs: ../../../../spring-data-commons/src/main/asciidoc
|
||||
|
||||
@@ -30,9 +22,8 @@ include::{spring-data-commons-docs}/repositories.adoc[]
|
||||
:leveloffset: +1
|
||||
include::reference/elasticsearch-clients.adoc[]
|
||||
include::reference/elasticsearch-object-mapping.adoc[]
|
||||
include::reference/data-elasticsearch.adoc[]
|
||||
include::reference/reactive-elasticsearch-operations.adoc[]
|
||||
include::reference/reactive-elasticsearch-repositories.adoc[]
|
||||
include::reference/elasticsearch-operations.adoc[]
|
||||
include::reference/elasticsearch-repositories.adoc[]
|
||||
include::reference/elasticsearch-misc.adoc[]
|
||||
:leveloffset: -1
|
||||
|
||||
|
||||
@@ -1,20 +1,44 @@
|
||||
[[preface]]
|
||||
= Preface
|
||||
|
||||
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.
|
||||
The Spring Data Elasticsearch project applies core Spring concepts to the development of solutions using the Elasticsearch Search Engine. It provides:
|
||||
|
||||
[[project]]
|
||||
[preface]
|
||||
* _Templates_ as a high-level abstraction for storing, querying, sorting and faceting documents.
|
||||
* _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 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
|
||||
|
||||
[[requirements]]
|
||||
[preface]
|
||||
[[preface.requirements]]
|
||||
== Requirements
|
||||
|
||||
Requires https://www.elastic.co/downloads/elasticsearch[Elasticsearch] 0.20.2 and above or optional dependency or not even that if you are using Embedded Node Client
|
||||
Requires an installation of https://www.elastic.co/products/elasticsearch[Elasticsearch].
|
||||
|
||||
[[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
|
||||
|Moore |3.2.x |6.8.4 |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>>.
|
||||
|
||||
@@ -1,288 +0,0 @@
|
||||
[[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
|
||||
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 `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
|
||||
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>
|
||||
----
|
||||
====
|
||||
|
||||
.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
|
||||
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: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);
|
||||
}
|
||||
----
|
||||
====
|
||||
@@ -3,14 +3,50 @@
|
||||
|
||||
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.
|
||||
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>>.
|
||||
|
||||
WARNING: The well known `TransportClient` is deprecated as of Elasticsearch 7.0.0 and is expected to be removed in Elasticsearch 8.0.
|
||||
[[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>>.
|
||||
|
||||
We strongly recommend to use the <<elasticsearch.clients.rest>> instead of the `TransportClient`.
|
||||
|
||||
.Transport Client
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
static class Config {
|
||||
|
||||
@Bean
|
||||
Client client() {
|
||||
Settings settings = Settings.builder()
|
||||
.put("cluster.name", "elasticsearch") <1>
|
||||
.build();
|
||||
TransportClient client = new PreBuiltTransportClient(settings);
|
||||
client.addTransportAddress(new TransportAddress(InetAddress.getByName("127.0.0.1")
|
||||
, 9300)); <2>
|
||||
return client;
|
||||
}
|
||||
}
|
||||
|
||||
// ...
|
||||
|
||||
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 provides a straight forward replacement for the `TransportClient` as it accepts and returns
|
||||
The Java High Level REST Client now 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.
|
||||
|
||||
@@ -18,6 +54,7 @@ Asynchronous calls are operated upon a client managed thread pool and require a
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
import org.springframework.beans.factory.annotation.Autowired;@Configuration
|
||||
static class Config {
|
||||
|
||||
@Bean
|
||||
@@ -27,20 +64,28 @@ static class Config {
|
||||
.connectedTo("localhost:9200", "localhost:9201")
|
||||
.build();
|
||||
|
||||
return RestClients.create(clientConfiguration).rest(); <2>
|
||||
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 = client.index(request);
|
||||
IndexResponse response = highLevelClient.index(request);
|
||||
----
|
||||
<1> Use the builder to provide cluster addresses, set default `HttpHeaders` or enbale SSL.
|
||||
<2> Next to the `rest()` client it is also possible to obtain the `lowLevelRest()` client.
|
||||
<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]]
|
||||
@@ -78,7 +123,7 @@ Mono<IndexResponse> response = client.index(request ->
|
||||
.setRefreshPolicy(IMMEDIATE);
|
||||
);
|
||||
----
|
||||
<1> Use the builder to provide cluster addresses, set default `HttpHeaders` or enbale SSL.
|
||||
<1> Use the builder to provide cluster addresses, set default `HttpHeaders` or enable SSL.
|
||||
====
|
||||
|
||||
NOTE: The ReactiveClient response, especially for search operations, is bound to the `from` (offset) & `size` (limit) options of the request.
|
||||
@@ -92,19 +137,28 @@ Client behaviour can be changed via the `ClientConfiguration` that allows to set
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
// optional if Basic Auhtentication is needed
|
||||
HttpHeaders defaultHeaders = new HttpHeaders();
|
||||
defaultHeaders.setBasicAuth(USER_NAME, USER_PASS); <1>
|
||||
|
||||
ClientConfiguration clientConfiguration = ClientConfiguration.builder()
|
||||
.connectedTo("localhost:9200", "localhost:9291") <1>
|
||||
.withConnectTimeout(Duration.ofSeconds(5)) <2>
|
||||
.withSocketTimeout(Duration.ofSeconds(3)) <3>
|
||||
.useSsl()
|
||||
.connectedTo("localhost:9200", "localhost:9291") <2>
|
||||
.withConnectTimeout(Duration.ofSeconds(5)) <3>
|
||||
.withSocketTimeout(Duration.ofSeconds(3)) <4>
|
||||
.useSsl() <5>
|
||||
.withDefaultHeaders(defaultHeaders) <6>
|
||||
.withBasicAuth(username, password) <7>
|
||||
. // ... other options
|
||||
.build();
|
||||
|
||||
----
|
||||
<1> Use the builder to provide cluster addresses, set default `HttpHeaders` or enbale SSL.
|
||||
<2> Set the connection timeout. Default is 10 sec.
|
||||
<3> Set the socket timeout. Default is 5 sec.
|
||||
<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> Set the connection timeout. Default is 10 sec.
|
||||
<4> Set the socket timeout. Default is 5 sec.
|
||||
<5> Optionally enable SSL.
|
||||
<6> Optionally set headers.
|
||||
<7> Add basic authentication.
|
||||
====
|
||||
|
||||
[[elasticsearch.clients.logging]]
|
||||
@@ -119,5 +173,4 @@ to be turned on as outlined in the snippet below.
|
||||
<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`.
|
||||
NOTE: The above applies to both the `RestHighLevelClient` and `ReactiveElasticsearchClient` when obtained via `RestClients` respectively `ReactiveRestClients`, is not available for the `TransportClient`.
|
||||
|
||||
@@ -14,12 +14,12 @@ Filter Builder improves query speed.
|
||||
private ElasticsearchTemplate elasticsearchTemplate;
|
||||
|
||||
SearchQuery searchQuery = new NativeSearchQueryBuilder()
|
||||
.withQuery(matchAllQuery())
|
||||
.withFilter(boolFilter().must(termFilter("id", documentId)))
|
||||
.build();
|
||||
|
||||
.withQuery(matchAllQuery())
|
||||
.withFilter(boolFilter().must(termFilter("id", documentId)))
|
||||
.build();
|
||||
|
||||
Page<SampleEntity> sampleEntities =
|
||||
elasticsearchTemplate.queryForPage(searchQuery,SampleEntity.class);
|
||||
elasticsearchTemplate.queryForPage(searchQuery,SampleEntity.class);
|
||||
----
|
||||
====
|
||||
|
||||
@@ -33,21 +33,21 @@ Elasticsearch has a scroll API for getting big result set in chunks. `Elasticsea
|
||||
[source,java]
|
||||
----
|
||||
SearchQuery searchQuery = new NativeSearchQueryBuilder()
|
||||
.withQuery(matchAllQuery())
|
||||
.withIndices(INDEX_NAME)
|
||||
.withTypes(TYPE_NAME)
|
||||
.withFields("message")
|
||||
.withPageable(PageRequest.of(0, 10))
|
||||
.build();
|
||||
.withQuery(matchAllQuery())
|
||||
.withIndices(INDEX_NAME)
|
||||
.withTypes(TYPE_NAME)
|
||||
.withFields("message")
|
||||
.withPageable(PageRequest.of(0, 10))
|
||||
.build();
|
||||
|
||||
ScrolledPage<SampleEntity> scroll = elasticsearchTemplate.startScroll(1000, searchQuery, SampleEntity.class);
|
||||
|
||||
String scrollId = scroll.getScrollId();
|
||||
List<SampleEntity> sampleEntities = new ArrayList<>();
|
||||
while (scroll.hasContent()) {
|
||||
sampleEntities.addAll(scroll.getContent());
|
||||
scrollId = scroll.getScrollId();
|
||||
scroll = elasticsearchTemplate.continueScroll(scrollId, 1000, SampleEntity.class);
|
||||
sampleEntities.addAll(scroll.getContent());
|
||||
scrollId = scroll.getScrollId();
|
||||
scroll = elasticsearchTemplate.continueScroll(scrollId, 1000, SampleEntity.class);
|
||||
}
|
||||
elasticsearchTemplate.clearScroll(scrollId);
|
||||
----
|
||||
@@ -60,18 +60,18 @@ elasticsearchTemplate.clearScroll(scrollId);
|
||||
[source,java]
|
||||
----
|
||||
SearchQuery searchQuery = new NativeSearchQueryBuilder()
|
||||
.withQuery(matchAllQuery())
|
||||
.withIndices(INDEX_NAME)
|
||||
.withTypes(TYPE_NAME)
|
||||
.withFields("message")
|
||||
.withPageable(PageRequest.of(0, 10))
|
||||
.build();
|
||||
.withQuery(matchAllQuery())
|
||||
.withIndices(INDEX_NAME)
|
||||
.withTypes(TYPE_NAME)
|
||||
.withFields("message")
|
||||
.withPageable(PageRequest.of(0, 10))
|
||||
.build();
|
||||
|
||||
CloseableIterator<SampleEntity> stream = elasticsearchTemplate.stream(searchQuery, SampleEntity.class);
|
||||
|
||||
List<SampleEntity> sampleEntities = new ArrayList<>();
|
||||
while (stream.hasNext()) {
|
||||
sampleEntities.add(stream.next());
|
||||
sampleEntities.add(stream.next());
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
[[new-features]]
|
||||
= What's new
|
||||
|
||||
[[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.
|
||||
@@ -21,14 +21,16 @@ public class Config extends AbstractElasticsearchConfiguration { <1>
|
||||
|
||||
@Override
|
||||
public RestHighLevelClient elasticsearchClient() {
|
||||
return RestClients.create(ClientConfiguration.create("localhost:9200")).rest()
|
||||
return RestClients.create(ClientConfiguration.create("localhost:9200")).rest();
|
||||
}
|
||||
}
|
||||
----
|
||||
<1> `AbstractElasticsearchConfiguration` already defines a Jackson2 based `entityMapper` via `ElasticsearchConfigurationSupport`.
|
||||
====
|
||||
|
||||
WARNING: `CustomConversions`, `@ReadingConverter` & `@WritingConverter` cannot be applied when using the Jackson based `EntityMapper`.
|
||||
[WARNING]
|
||||
`CustomConversions`, `@ReadingConverter` & `@WritingConverter` cannot be applied when using the Jackson based `EntityMapper`. +
|
||||
Setting the name of a mapped field with `@Field(name="custom-name")` also cannot be used with this Mapper.
|
||||
|
||||
[[elasticsearch.mapping.meta-model]]
|
||||
== Meta Model Object Mapping
|
||||
@@ -50,11 +52,12 @@ public class Config extends AbstractElasticsearchConfiguration {
|
||||
|
||||
@Bean
|
||||
@Override
|
||||
public EntityMapper entityMapper() { <1>
|
||||
public EntityMapper entityMapper() { <1>
|
||||
|
||||
ElasticsearchEntityMapper entityMapper = new ElasticsearchEntityMapper(elasticsearchMappingContext(),
|
||||
new DefaultConversionService()); <2>
|
||||
entityMapper.setConversions(elasticsearchCustomConversions()); <3>
|
||||
ElasticsearchEntityMapper entityMapper = new ElasticsearchEntityMapper(
|
||||
elasticsearchMappingContext(), new DefaultConversionService() <2>
|
||||
);
|
||||
entityMapper.setConversions(elasticsearchCustomConversions()); <3>
|
||||
|
||||
return entityMapper;
|
||||
}
|
||||
@@ -72,10 +75,26 @@ for `Converter` registration.
|
||||
The `ElasticsearchEntityMapper` can use metadata to drive the mapping of objects to documents. The following annotations are available:
|
||||
|
||||
* `@Id`: Applied at the field level to mark the field used for identity purpose.
|
||||
* `@Document`: Applied at the class level to indicate this class is a candidate for mapping to the database. You can specify the index name and index type where the document will be stored.
|
||||
* `@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_.
|
||||
* `@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 described the name of the field as it will be represented in the Elasticsearch document thus allowing the name to be different than the fieldname of the class.
|
||||
* `@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_.
|
||||
** `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.
|
||||
|
||||
@@ -91,7 +110,7 @@ Those type hints are represented as `_class` attributes within the document and
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
public class Person { <1>
|
||||
public class Person { <1>
|
||||
|
||||
@Id String id;
|
||||
String firstname;
|
||||
@@ -119,7 +138,7 @@ to already have entity information available when first reading data from the st
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
@TypeAlias("human") <1>
|
||||
@TypeAlias("human") <1>
|
||||
public class Person {
|
||||
|
||||
@Id String id;
|
||||
@@ -129,7 +148,7 @@ public class Person {
|
||||
[source,json]
|
||||
----
|
||||
{
|
||||
"_class" : "human", <1>
|
||||
"_class" : "human", <1>
|
||||
"id" : ...
|
||||
}
|
||||
----
|
||||
@@ -235,16 +254,16 @@ public class Config extends AbstractElasticsearchConfiguration {
|
||||
|
||||
@Override
|
||||
public RestHighLevelClient elasticsearchClient() {
|
||||
return RestClients.create(ClientConfiguration.create("localhost:9200")).rest()
|
||||
return RestClients.create(ClientConfiguration.create("localhost:9200")).rest();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Override
|
||||
public EntityMapper entityMapper() {
|
||||
|
||||
ElasticsearchEntityMapper entityMapper = new ElasticsearchEntityMapper(elasticsearchMappingContext(),
|
||||
new DefaultConversionService());
|
||||
entityMapper.setConversions(elasticsearchCustomConversions()); <1>
|
||||
ElasticsearchEntityMapper entityMapper = new ElasticsearchEntityMapper(
|
||||
elasticsearchMappingContext(), new DefaultConversionService());
|
||||
entityMapper.setConversions(elasticsearchCustomConversions()); <1>
|
||||
|
||||
return entityMapper;
|
||||
}
|
||||
@@ -252,10 +271,11 @@ public class Config extends AbstractElasticsearchConfiguration {
|
||||
@Bean
|
||||
@Override
|
||||
public ElasticsearchCustomConversions elasticsearchCustomConversions() {
|
||||
return new ElasticsearchCustomConversions(Arrays.asList(new AddressToMap(), new MapToAddress())); <2>
|
||||
return new ElasticsearchCustomConversions(
|
||||
Arrays.asList(new AddressToMap(), new MapToAddress())); <2>
|
||||
}
|
||||
|
||||
@WritingConverter <3>
|
||||
@WritingConverter <3>
|
||||
static class AddressToMap implements Converter<Address, Map<String, Object>> {
|
||||
|
||||
@Override
|
||||
@@ -269,7 +289,7 @@ public class Config extends AbstractElasticsearchConfiguration {
|
||||
}
|
||||
}
|
||||
|
||||
@ReadingConverter <4>
|
||||
@ReadingConverter <4>
|
||||
static class MapToAddress implements Converter<Map<String, Object>, Address> {
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
[[elasticsearch.operations]]
|
||||
= Elasticsearch Operations
|
||||
|
||||
Spring Data Elasticsearch uses two interfaces to define the operations that can be called against an Elasticsearch index. These are `ElasticsearchOperations` and `ReactiveElasticsearchOperations`. Whereas the first is used with the classic synchronous implementations, the second one uses reactive infrastructure.
|
||||
|
||||
The default implementations of the interfaces offer:
|
||||
|
||||
* Read/Write mapping support for domain types.
|
||||
* A rich query and criteria api.
|
||||
* Resource management and Exception translation.
|
||||
|
||||
[[elasticsearch.operations.template]]
|
||||
== ElasticsearchTemplate
|
||||
|
||||
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(), entityMapper());
|
||||
}
|
||||
|
||||
// use the ElasticsearchEntityMapper
|
||||
@Bean
|
||||
@Override
|
||||
public EntityMapper entityMapper() { <3>
|
||||
ElasticsearchEntityMapper entityMapper = new ElasticsearchEntityMapper(elasticsearchMappingContext(),
|
||||
new DefaultConversionService());
|
||||
entityMapper.setConversions(elasticsearchCustomConversions());
|
||||
return entityMapper;
|
||||
}
|
||||
}
|
||||
----
|
||||
<1> Setting up the <<elasticsearch.clients.transport>>.
|
||||
<2> Creating the `ElasticsearchTemplate` bean, offering both names, _elasticsearchOperations_ and _elasticsearchTemplate_.
|
||||
<3> Using the <<elasticsearch.mapping.meta-model>> ElasticsearchMapper.
|
||||
====
|
||||
|
||||
[[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>
|
||||
|
||||
// use the ElasticsearchEntityMapper
|
||||
@Bean
|
||||
@Override
|
||||
public EntityMapper entityMapper() { <3>
|
||||
ElasticsearchEntityMapper entityMapper = new ElasticsearchEntityMapper(elasticsearchMappingContext(),
|
||||
new DefaultConversionService());
|
||||
entityMapper.setConversions(elasticsearchCustomConversions());
|
||||
|
||||
return entityMapper;
|
||||
}
|
||||
}
|
||||
----
|
||||
<1> Setting up the <<elasticsearch.clients.rest>>.
|
||||
<2> The base class `AbstractElasticsearchConfiguration` already provides the `elasticsearchTemplate` bean.
|
||||
<3> Using the <<elasticsearch.mapping.meta-model>> ElasticsearchMapper.
|
||||
====
|
||||
|
||||
[[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]
|
||||
@@ -0,0 +1,149 @@
|
||||
[[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]
|
||||
@@ -0,0 +1,156 @@
|
||||
[[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 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]
|
||||
----
|
||||
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]
|
||||
----
|
||||
interface BookRepository extends ElasticsearchRepository<Book, String> {
|
||||
@Query("{\"bool\" : {\"must\" : {\"field\" : {\"name\" : \"?0\"}}}}")
|
||||
Page<Book> findByName(String name,Pageable pageable);
|
||||
}
|
||||
----
|
||||
====
|
||||
@@ -3,11 +3,7 @@
|
||||
|
||||
`ReactiveElasticsearchOperations` is the gateway to executing high level commands against an Elasticsearch cluster using the `ReactiveElasticsearchClient`.
|
||||
|
||||
The `ReactiveElasticsearchTemplate` is the default implementation of `ReactiveElasticsearchOperations` and offers the following set of features.
|
||||
|
||||
* Read/Write mapping support for domain types.
|
||||
* A rich query and criteria api.
|
||||
* Resource management and Exception translation.
|
||||
The `ReactiveElasticsearchTemplate` is the default implementation of `ReactiveElasticsearchOperations`.
|
||||
|
||||
[[elasticsearch.reactive.template]]
|
||||
== Reactive Elasticsearch Template
|
||||
@@ -28,11 +24,11 @@ dedicated configuration method hooks for `base package`, the `initial entity set
|
||||
@Configuration
|
||||
public class Config extends AbstractReactiveElasticsearchConfiguration {
|
||||
|
||||
@Bean <1>
|
||||
@Override
|
||||
public ReactiveElasticsearchClient reactiveElasticsearchClient() {
|
||||
// ...
|
||||
}
|
||||
@Bean <1>
|
||||
@Override
|
||||
public ReactiveElasticsearchClient reactiveElasticsearchClient() {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
----
|
||||
<1> Configure the client to use. This can be done by `ReactiveRestClients` or directly via `DefaultReactiveElasticsearchClient`.
|
||||
@@ -51,25 +47,22 @@ However one might want to be more in control over the actual components and use
|
||||
@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());
|
||||
}
|
||||
@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`.
|
||||
@@ -92,25 +85,24 @@ Consider the following:
|
||||
@Document(indexName = "marvel", type = "characters")
|
||||
public class Person {
|
||||
|
||||
private @Id String id;
|
||||
private String name;
|
||||
private int age;
|
||||
|
||||
// Getter/Setter omitted...
|
||||
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>
|
||||
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.
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
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 Elasticsearchs reactive repository support uses https://projectreactor.io/[Project Reactor] as its 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:
|
||||
@@ -42,7 +42,7 @@ NOTE: Please note that the `id` property needs to be of type `String`.
|
||||
====
|
||||
[source]
|
||||
----
|
||||
public interface ReactivePersonRepository extends ReactiveSortingRepository<Person, String> {
|
||||
interface ReactivePersonRepository extends ReactiveSortingRepository<Person, String> {
|
||||
|
||||
Flux<Person> findByFirstname(String firstname); <1>
|
||||
|
||||
|
||||
@@ -15,41 +15,81 @@
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.annotations;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Document
|
||||
* Identifies a domain object to be persisted to Elasticsearch.
|
||||
*
|
||||
* @author Rizwan Idrees
|
||||
* @author Mohsin Husen
|
||||
* @author Mason Chan
|
||||
* @author Ivan Greene
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
|
||||
@Persistent
|
||||
@Inherited
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.TYPE})
|
||||
@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.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
short shards() default 5;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
@@ -19,12 +19,16 @@ package org.springframework.data.elasticsearch.annotations;
|
||||
* @author Rizwan Idrees
|
||||
* @author Mohsin Husen
|
||||
* @author Artur Konczak
|
||||
* @author Zeng Zetang
|
||||
*/
|
||||
public enum FieldType {
|
||||
Text,
|
||||
Byte,
|
||||
Short,
|
||||
Integer,
|
||||
Long,
|
||||
Date,
|
||||
Half_Float,
|
||||
Float,
|
||||
Double,
|
||||
Boolean,
|
||||
|
||||
@@ -21,6 +21,7 @@ import java.time.Duration;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
import javax.net.ssl.SSLContext;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
@@ -29,6 +30,8 @@ import org.springframework.http.HttpHeaders;
|
||||
* Configuration interface exposing common client configuration properties for Elasticsearch clients.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @author Peter-Josef Meisch
|
||||
* @author Henrique Amaral
|
||||
* @since 3.2
|
||||
*/
|
||||
public interface ClientConfiguration {
|
||||
@@ -117,6 +120,13 @@ public interface ClientConfiguration {
|
||||
*/
|
||||
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}.
|
||||
*
|
||||
@@ -134,6 +144,14 @@ public interface ClientConfiguration {
|
||||
*/
|
||||
Duration getSocketTimeout();
|
||||
|
||||
/**
|
||||
* returns an optionally set proxy in the form host:port
|
||||
*
|
||||
* @return the optional proxy
|
||||
* @since 4.0
|
||||
*/
|
||||
Optional<String> getProxy();
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
@@ -200,6 +218,16 @@ public interface ClientConfiguration {
|
||||
* @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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -257,6 +285,21 @@ public interface ClientConfiguration {
|
||||
*/
|
||||
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);
|
||||
|
||||
/**
|
||||
* @param proxy a proxy formatted as String {@literal host:port}.
|
||||
* @return the {@link MaybeSecureClientConfigurationBuilder}.
|
||||
*/
|
||||
MaybeSecureClientConfigurationBuilder withProxy(String proxy);
|
||||
|
||||
/**
|
||||
* Build the {@link ClientConfiguration} object.
|
||||
*
|
||||
|
||||
+56
-3
@@ -22,6 +22,7 @@ import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
import javax.net.ssl.SSLContext;
|
||||
|
||||
import org.springframework.data.elasticsearch.client.ClientConfiguration.ClientConfigurationBuilderWithRequiredEndpoint;
|
||||
@@ -36,6 +37,8 @@ import org.springframework.util.Assert;
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
* @author Peter-Josef Meisch
|
||||
* @author Henrique Amaral
|
||||
* @since 3.2
|
||||
*/
|
||||
class ClientConfigurationBuilder
|
||||
@@ -45,8 +48,12 @@ class ClientConfigurationBuilder
|
||||
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 String username;
|
||||
private String password;
|
||||
private String proxy;
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
@@ -75,6 +82,13 @@ class ClientConfigurationBuilder
|
||||
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()
|
||||
@@ -100,6 +114,22 @@ class ClientConfigurationBuilder
|
||||
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)
|
||||
@@ -109,7 +139,9 @@ class ClientConfigurationBuilder
|
||||
|
||||
Assert.notNull(defaultHeaders, "Default HTTP headers must not be null");
|
||||
|
||||
this.headers = defaultHeaders;
|
||||
this.headers = new HttpHeaders();
|
||||
this.headers.addAll(defaultHeaders);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -139,17 +171,38 @@ class ClientConfigurationBuilder
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.client.ClientConfiguration.ClientConfigurationBuilderWithOptionalDefaultHeaders#build()
|
||||
*/
|
||||
@Override
|
||||
public ClientConfiguration build() {
|
||||
return new DefaultClientConfiguration(this.hosts, this.headers, this.useSsl, this.sslContext, this.soTimeout,
|
||||
this.connectTimeout);
|
||||
|
||||
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,
|
||||
hostnameVerifier, proxy);
|
||||
}
|
||||
|
||||
private static InetSocketAddress parse(String hostAndPort) {
|
||||
return InetSocketAddressParser.parse(hostAndPort, ElasticsearchHost.DEFAULT_PORT);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+24
-1
@@ -22,6 +22,7 @@ import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
import javax.net.ssl.SSLContext;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
@@ -42,9 +43,12 @@ class DefaultClientConfiguration implements ClientConfiguration {
|
||||
private final @Nullable SSLContext sslContext;
|
||||
private final Duration soTimeout;
|
||||
private final Duration connectTimeout;
|
||||
private final @Nullable HostnameVerifier hostnameVerifier;
|
||||
private final String proxy;
|
||||
|
||||
DefaultClientConfiguration(List<InetSocketAddress> hosts, HttpHeaders headers, boolean useSsl,
|
||||
@Nullable SSLContext sslContext, Duration soTimeout, Duration connectTimeout) {
|
||||
@Nullable SSLContext sslContext, Duration soTimeout, Duration connectTimeout,
|
||||
@Nullable HostnameVerifier hostnameVerifier, String proxy) {
|
||||
|
||||
this.hosts = Collections.unmodifiableList(new ArrayList<>(hosts));
|
||||
this.headers = new HttpHeaders(headers);
|
||||
@@ -52,6 +56,8 @@ class DefaultClientConfiguration implements ClientConfiguration {
|
||||
this.sslContext = sslContext;
|
||||
this.soTimeout = soTimeout;
|
||||
this.connectTimeout = connectTimeout;
|
||||
this.hostnameVerifier = hostnameVerifier;
|
||||
this.proxy = proxy;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -90,6 +96,15 @@ class DefaultClientConfiguration implements ClientConfiguration {
|
||||
return Optional.ofNullable(this.sslContext);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.client.ClientConfiguration#getHostNameVerifier()
|
||||
*/
|
||||
@Override
|
||||
public Optional<HostnameVerifier> getHostNameVerifier() {
|
||||
return Optional.ofNullable(this.hostnameVerifier);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.client.ClientConfiguration#getConnectTimeout()
|
||||
@@ -108,4 +123,12 @@ class DefaultClientConfiguration implements ClientConfiguration {
|
||||
return this.soTimeout;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.client.ClientConfiguration#getProxy()
|
||||
*/
|
||||
@Override
|
||||
public Optional<String> getProxy() {
|
||||
return Optional.ofNullable(proxy);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ 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;
|
||||
@@ -52,6 +53,7 @@ import org.springframework.util.Assert;
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
* @author Henrique Amaral
|
||||
* @since 3.2
|
||||
*/
|
||||
public final class RestClients {
|
||||
@@ -87,7 +89,9 @@ public final class RestClients {
|
||||
builder.setHttpClientConfigCallback(clientBuilder -> {
|
||||
|
||||
Optional<SSLContext> sslContext = clientConfiguration.getSslContext();
|
||||
Optional<HostnameVerifier> hostNameVerifier = clientConfiguration.getHostNameVerifier();
|
||||
sslContext.ifPresent(clientBuilder::setSSLContext);
|
||||
hostNameVerifier.ifPresent(clientBuilder::setSSLHostnameVerifier);
|
||||
|
||||
if (ClientLogger.isEnabled()) {
|
||||
|
||||
@@ -114,6 +118,8 @@ public final class RestClients {
|
||||
|
||||
clientBuilder.setDefaultRequestConfig(requestConfigBuilder.build());
|
||||
|
||||
clientConfiguration.getProxy().map(HttpHost::create).ifPresent(clientBuilder::setProxy);
|
||||
|
||||
return clientBuilder;
|
||||
});
|
||||
|
||||
|
||||
+1
-1
@@ -72,7 +72,7 @@ public class TransportClientFactoryBean implements FactoryBean<TransportClient>,
|
||||
|
||||
@Override
|
||||
public boolean isSingleton() {
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+45
-5
@@ -47,7 +47,6 @@ import javax.net.ssl.SSLContext;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
import org.elasticsearch.ElasticsearchStatusException;
|
||||
import org.elasticsearch.action.ActionRequest;
|
||||
import org.elasticsearch.action.ActionResponse;
|
||||
import org.elasticsearch.action.admin.indices.close.CloseIndexRequest;
|
||||
import org.elasticsearch.action.admin.indices.create.CreateIndexRequest;
|
||||
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
|
||||
@@ -58,6 +57,8 @@ 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;
|
||||
@@ -77,6 +78,8 @@ 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.client.core.CountResponse;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.common.xcontent.DeprecationHandler;
|
||||
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
|
||||
@@ -122,6 +125,7 @@ import org.springframework.web.reactive.function.client.WebClient.RequestBodySpe
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
* @author Henrique Amaral
|
||||
* @since 3.2
|
||||
* @see ClientConfiguration
|
||||
* @see ReactiveRestClients
|
||||
@@ -319,6 +323,17 @@ public class DefaultReactiveElasticsearchClient implements ReactiveElasticsearch
|
||||
.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, CountRequest countRequest) {
|
||||
return sendRequest(countRequest, RequestCreator.count(), CountResponse.class, headers) //
|
||||
.map(CountResponse::getCount) //
|
||||
.next();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.client.reactive.ReactiveElasticsearchClient#ping(org.springframework.http.HttpHeaders, org.elasticsearch.action.search.SearchRequest)
|
||||
@@ -423,6 +438,16 @@ public class DefaultReactiveElasticsearchClient implements ReactiveElasticsearch
|
||||
.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
|
||||
|
||||
/*
|
||||
@@ -553,13 +578,12 @@ public class DefaultReactiveElasticsearchClient implements ReactiveElasticsearch
|
||||
|
||||
// -->
|
||||
|
||||
private <Req extends ActionRequest, Resp extends ActionResponse> Flux<Resp> sendRequest(Req request,
|
||||
Function<Req, Request> converter, Class<Resp> responseType, HttpHeaders headers) {
|
||||
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 <AR extends ActionResponse> Flux<AR> sendRequest(Request request, Class<AR> responseType,
|
||||
HttpHeaders headers) {
|
||||
private <Resp> Flux<Resp> sendRequest(Request request, Class<Resp> responseType, HttpHeaders headers) {
|
||||
|
||||
String logId = ClientLogger.newLogId();
|
||||
|
||||
@@ -742,6 +766,18 @@ public class DefaultReactiveElasticsearchClient implements ReactiveElasticsearch
|
||||
};
|
||||
}
|
||||
|
||||
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() {
|
||||
@@ -776,6 +812,10 @@ public class DefaultReactiveElasticsearchClient implements ReactiveElasticsearch
|
||||
return RequestConverters::flushIndex;
|
||||
}
|
||||
|
||||
static Function<CountRequest, Request> count() {
|
||||
return RequestConverters::count;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+6
-119
@@ -15,29 +15,22 @@
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.client.reactive;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.elasticsearch.action.ActionResponse;
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseCookie;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.http.client.reactive.ClientHttpResponse;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.web.reactive.function.BodyExtractor;
|
||||
import org.springframework.web.reactive.function.client.ClientResponse;
|
||||
import org.springframework.web.reactive.function.client.ExchangeStrategies;
|
||||
|
||||
/**
|
||||
* Extension to {@link ActionResponse} that also implements {@link 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 implements ClientResponse {
|
||||
class RawActionResponse extends ActionResponse {
|
||||
|
||||
private final ClientResponse delegate;
|
||||
|
||||
@@ -49,129 +42,23 @@ class RawActionResponse extends ActionResponse implements ClientResponse {
|
||||
return new RawActionResponse(response);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.web.reactive.function.client.ClientResponse#statusCode()
|
||||
*/
|
||||
@Override
|
||||
public HttpStatus statusCode() {
|
||||
return delegate.statusCode();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.web.reactive.function.client.ClientResponse#rawStatusCode()
|
||||
*/
|
||||
@Override
|
||||
public int rawStatusCode() {
|
||||
return delegate.rawStatusCode();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.web.reactive.function.client.ClientResponse#headers()
|
||||
*/
|
||||
@Override
|
||||
public Headers headers() {
|
||||
public ClientResponse.Headers headers() {
|
||||
return delegate.headers();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.web.reactive.function.client.ClientResponse#cookies()
|
||||
*/
|
||||
@Override
|
||||
public MultiValueMap<String, ResponseCookie> cookies() {
|
||||
return delegate.cookies();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.web.reactive.function.client.ClientResponse#strategies()
|
||||
*/
|
||||
@Override
|
||||
public ExchangeStrategies strategies() {
|
||||
return delegate.strategies();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.web.reactive.function.client.ClientResponse#body(org.springframework.web.reactive.function.BodyExtractor)
|
||||
*/
|
||||
@Override
|
||||
public <T> T body(BodyExtractor<T, ? super ClientHttpResponse> extractor) {
|
||||
return delegate.body(extractor);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.web.reactive.function.client.ClientResponse#bodyToMono(java.lang.Class)
|
||||
*/
|
||||
@Override
|
||||
public <T> Mono<T> bodyToMono(Class<? extends T> elementClass) {
|
||||
return delegate.bodyToMono(elementClass);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.web.reactive.function.client.ClientResponse#bodyToMono(org.springframework.core.ParameterizedTypeReference)
|
||||
*/
|
||||
@Override
|
||||
public <T> Mono<T> bodyToMono(ParameterizedTypeReference<T> typeReference) {
|
||||
return delegate.bodyToMono(typeReference);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.web.reactive.function.client.ClientResponse#bodyToFlux(java.lang.Class)
|
||||
*/
|
||||
@Override
|
||||
public <T> Flux<T> bodyToFlux(Class<? extends T> elementClass) {
|
||||
return delegate.bodyToFlux(elementClass);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.web.reactive.function.client.ClientResponse#bodyToFlux(org.springframework.core.ParameterizedTypeReference)
|
||||
*/
|
||||
@Override
|
||||
public <T> Flux<T> bodyToFlux(ParameterizedTypeReference<T> typeReference) {
|
||||
return delegate.bodyToFlux(typeReference);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.web.reactive.function.client.ClientResponse#toEntity(java.lang.Class)
|
||||
*/
|
||||
@Override
|
||||
public <T> Mono<ResponseEntity<T>> toEntity(Class<T> bodyType) {
|
||||
return delegate.toEntity(bodyType);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.web.reactive.function.client.ClientResponse#toEntity(org.springframework.core.ParameterizedTypeReference)
|
||||
*/
|
||||
@Override
|
||||
public <T> Mono<ResponseEntity<T>> toEntity(ParameterizedTypeReference<T> typeReference) {
|
||||
return delegate.toEntity(typeReference);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.web.reactive.function.client.ClientResponse#toEntityList(java.lang.Class)
|
||||
*/
|
||||
@Override
|
||||
public <T> Mono<ResponseEntity<List<T>>> toEntityList(Class<T> elementType) {
|
||||
return delegate.toEntityList(elementType);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.web.reactive.function.client.ClientResponse#toEntityList(org.springframework.core.ParameterizedTypeReference)
|
||||
*/
|
||||
@Override
|
||||
public <T> Mono<ResponseEntity<List<T>>> toEntityList(ParameterizedTypeReference<T> typeReference) {
|
||||
return delegate.toEntityList(typeReference);
|
||||
}
|
||||
}
|
||||
|
||||
+84
@@ -30,6 +30,8 @@ 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;
|
||||
@@ -41,6 +43,7 @@ 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.client.core.CountRequest;
|
||||
import org.elasticsearch.index.get.GetResult;
|
||||
import org.elasticsearch.index.reindex.BulkByScrollResponse;
|
||||
import org.elasticsearch.index.reindex.DeleteByQueryRequest;
|
||||
@@ -58,6 +61,8 @@ import org.springframework.web.reactive.function.client.WebClient;
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
* @author Peter-Josef Meisch
|
||||
* @author Henrique Amaral
|
||||
* @since 3.2
|
||||
* @see ClientConfiguration
|
||||
* @see ReactiveRestClients
|
||||
@@ -329,6 +334,47 @@ public interface ReactiveElasticsearchClient {
|
||||
*/
|
||||
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<CountRequest> consumer) {
|
||||
|
||||
CountRequest countRequest = new CountRequest();
|
||||
consumer.accept(countRequest);
|
||||
return count(countRequest);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a {@link SearchRequest} against the {@literal count} API.
|
||||
*
|
||||
* @param countRequest 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(CountRequest countRequest) {
|
||||
return count(HttpHeaders.EMPTY, countRequest);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a {@link SearchRequest} against the {@literal count} API.
|
||||
*
|
||||
* @param headers Use {@link HttpHeaders} to provide eg. authentication data. Must not be {@literal null}.
|
||||
* @param countRequest 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, CountRequest countRequest);
|
||||
|
||||
/**
|
||||
* Execute a {@link SearchRequest} against the {@literal search} API.
|
||||
*
|
||||
@@ -430,6 +476,44 @@ public interface ReactiveElasticsearchClient {
|
||||
*/
|
||||
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
|
||||
|
||||
+49
-1
@@ -60,8 +60,8 @@ import org.elasticsearch.action.update.UpdateRequest;
|
||||
import org.elasticsearch.client.Request;
|
||||
import org.elasticsearch.client.Requests;
|
||||
import org.elasticsearch.client.RethrottleRequest;
|
||||
import org.elasticsearch.client.core.CountRequest;
|
||||
import org.elasticsearch.cluster.health.ClusterHealthStatus;
|
||||
import org.elasticsearch.common.Nullable;
|
||||
import org.elasticsearch.common.Priority;
|
||||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.common.SuppressForbidden;
|
||||
@@ -82,10 +82,12 @@ import org.elasticsearch.index.reindex.AbstractBulkByScrollRequest;
|
||||
import org.elasticsearch.index.reindex.DeleteByQueryRequest;
|
||||
import org.elasticsearch.index.reindex.ReindexRequest;
|
||||
import org.elasticsearch.index.reindex.UpdateByQueryRequest;
|
||||
import org.elasticsearch.index.seqno.SequenceNumbers;
|
||||
import org.elasticsearch.search.fetch.subphase.FetchSourceContext;
|
||||
import org.elasticsearch.tasks.TaskId;
|
||||
import org.springframework.data.elasticsearch.client.reactive.ReactiveElasticsearchClient;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
@@ -97,6 +99,8 @@ import org.springframework.http.HttpMethod;
|
||||
* <p>
|
||||
* Only intended for internal use.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Peter-Josef Meisch
|
||||
* @since 3.2
|
||||
*/
|
||||
public class RequestConverters {
|
||||
@@ -116,6 +120,8 @@ public class RequestConverters {
|
||||
parameters.withTimeout(deleteRequest.timeout());
|
||||
parameters.withVersion(deleteRequest.version());
|
||||
parameters.withVersionType(deleteRequest.versionType());
|
||||
parameters.withIfSeqNo(deleteRequest.ifSeqNo());
|
||||
parameters.withIfPrimaryTerm(deleteRequest.ifPrimaryTerm());
|
||||
parameters.withRefreshPolicy(deleteRequest.getRefreshPolicy());
|
||||
parameters.withWaitForActiveShards(deleteRequest.waitForActiveShards());
|
||||
return request;
|
||||
@@ -315,6 +321,8 @@ public class RequestConverters {
|
||||
parameters.withTimeout(indexRequest.timeout());
|
||||
parameters.withVersion(indexRequest.version());
|
||||
parameters.withVersionType(indexRequest.versionType());
|
||||
parameters.withIfSeqNo(indexRequest.ifSeqNo());
|
||||
parameters.withIfPrimaryTerm(indexRequest.ifPrimaryTerm());
|
||||
parameters.withPipeline(indexRequest.getPipeline());
|
||||
parameters.withRefreshPolicy(indexRequest.getRefreshPolicy());
|
||||
parameters.withWaitForActiveShards(indexRequest.waitForActiveShards());
|
||||
@@ -381,6 +389,32 @@ public class RequestConverters {
|
||||
return request;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a count request.
|
||||
*
|
||||
* @param countRequest the search defining the data to be counted
|
||||
* @return Elasticsearch count request
|
||||
* @since 4.0
|
||||
*/
|
||||
public static Request count(CountRequest countRequest) {
|
||||
Request request = new Request(HttpMethod.POST.name(),
|
||||
endpoint(countRequest.indices(), countRequest.types(), "_count"));
|
||||
|
||||
Params params = new Params(request);
|
||||
addCountRequestParams(params, countRequest);
|
||||
|
||||
if (countRequest.source() != null) {
|
||||
request.setEntity(createEntity(countRequest.source(), REQUEST_BODY_CONTENT_TYPE));
|
||||
}
|
||||
return request;
|
||||
}
|
||||
|
||||
private static void addCountRequestParams(Params params, CountRequest countRequest) {
|
||||
params.withRouting(countRequest.routing());
|
||||
params.withPreference(countRequest.preference());
|
||||
params.withIndicesOptions(countRequest.indicesOptions());
|
||||
}
|
||||
|
||||
private static void addSearchRequestParams(Params params, SearchRequest searchRequest) {
|
||||
params.putParam("typed_keys", "true");
|
||||
params.withRouting(searchRequest.routing());
|
||||
@@ -917,6 +951,20 @@ public class RequestConverters {
|
||||
return this;
|
||||
}
|
||||
|
||||
Params withIfSeqNo(long seqNo) {
|
||||
if (seqNo != SequenceNumbers.UNASSIGNED_SEQ_NO) {
|
||||
return putParam("if_seq_no", Long.toString(seqNo));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
Params withIfPrimaryTerm(long primaryTerm) {
|
||||
if (primaryTerm != SequenceNumbers.UNASSIGNED_PRIMARY_TERM) {
|
||||
return putParam("if_primary_term", Long.toString(primaryTerm));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
Params withWaitForActiveShards(ActiveShardCount activeShardCount) {
|
||||
return withWaitForActiveShards(activeShardCount, ActiveShardCount.DEFAULT);
|
||||
}
|
||||
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
package org.springframework.data.elasticsearch.core;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.data.elasticsearch.ElasticsearchException;
|
||||
import org.springframework.data.elasticsearch.annotations.Mapping;
|
||||
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* AbstractElasticsearchTemplate
|
||||
*
|
||||
* @author Sascha Woo
|
||||
*/
|
||||
public abstract class AbstractElasticsearchTemplate {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(AbstractElasticsearchTemplate.class);
|
||||
|
||||
protected ElasticsearchConverter elasticsearchConverter;
|
||||
|
||||
public AbstractElasticsearchTemplate(ElasticsearchConverter elasticsearchConverter) {
|
||||
|
||||
Assert.notNull(elasticsearchConverter, "elasticsearchConverter must not be null.");
|
||||
this.elasticsearchConverter = elasticsearchConverter;
|
||||
}
|
||||
|
||||
protected String 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 mappings;
|
||||
}
|
||||
} else {
|
||||
LOGGER.info("mappingPath in @Mapping has to be defined. Building mappings using @Field");
|
||||
}
|
||||
}
|
||||
|
||||
// build mapping from field annotations
|
||||
try {
|
||||
MappingBuilder mappingBuilder = new MappingBuilder(elasticsearchConverter);
|
||||
return mappingBuilder.buildPropertyMapping(clazz);
|
||||
} catch (Exception e) {
|
||||
throw new ElasticsearchException("Failed to build mapping for " + clazz.getSimpleName(), e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -20,7 +20,6 @@ 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.elasticsearch.action.get.GetResponse;
|
||||
@@ -29,6 +28,7 @@ import org.elasticsearch.action.get.MultiGetResponse;
|
||||
import org.elasticsearch.action.search.SearchResponse;
|
||||
import org.elasticsearch.common.document.DocumentField;
|
||||
import org.elasticsearch.search.SearchHit;
|
||||
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
@@ -189,8 +189,8 @@ public class DefaultResultMapper extends AbstractResultMapper {
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> LinkedList<T> mapResults(MultiGetResponse responses, Class<T> clazz) {
|
||||
LinkedList<T> list = new LinkedList<>();
|
||||
public <T> List<T> mapResults(MultiGetResponse responses, Class<T> clazz) {
|
||||
List<T> list = new ArrayList<>();
|
||||
for (MultiGetItemResponse response : responses.getResponses()) {
|
||||
if (!response.isFailed() && response.getResponse().isExists()) {
|
||||
T result = mapEntity(response.getResponse().getSourceAsString(), clazz);
|
||||
|
||||
+114
-70
@@ -15,20 +15,28 @@
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.core;
|
||||
|
||||
import org.elasticsearch.action.update.UpdateResponse;
|
||||
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;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.elasticsearch.action.update.UpdateResponse;
|
||||
import org.elasticsearch.cluster.metadata.AliasMetaData;
|
||||
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.AliasQuery;
|
||||
import org.springframework.data.elasticsearch.core.query.BulkOptions;
|
||||
import org.springframework.data.elasticsearch.core.query.CriteriaQuery;
|
||||
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.MoreLikeThisQuery;
|
||||
import org.springframework.data.elasticsearch.core.query.SearchQuery;
|
||||
import org.springframework.data.elasticsearch.core.query.StringQuery;
|
||||
import org.springframework.data.elasticsearch.core.query.UpdateQuery;
|
||||
import org.springframework.data.util.CloseableIterator;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* ElasticsearchOperations
|
||||
*
|
||||
@@ -37,13 +45,25 @@ import java.util.stream.Collectors;
|
||||
* @author Kevin Leturc
|
||||
* @author Zetang Zeng
|
||||
* @author Dmitriy Yakovlev
|
||||
* @author Peter-Josef Meisch
|
||||
*/
|
||||
public interface ElasticsearchOperations {
|
||||
|
||||
/**
|
||||
* @return Converter in use
|
||||
* adding new alias
|
||||
*
|
||||
* @param query
|
||||
* @return
|
||||
*/
|
||||
ElasticsearchConverter getElasticsearchConverter();
|
||||
boolean addAlias(AliasQuery query);
|
||||
|
||||
/**
|
||||
* removing previously created alias
|
||||
*
|
||||
* @param query
|
||||
* @return
|
||||
*/
|
||||
boolean removeAlias(AliasQuery query);
|
||||
|
||||
/**
|
||||
* Create an index for a class
|
||||
@@ -84,6 +104,16 @@ public interface ElasticsearchOperations {
|
||||
*/
|
||||
<T> boolean putMapping(Class<T> clazz);
|
||||
|
||||
/**
|
||||
* Create mapping for the given class and put the mapping to the given indexName and type.
|
||||
*
|
||||
* @param indexName
|
||||
* @param type
|
||||
* @param mappings
|
||||
* @since 3.2
|
||||
*/
|
||||
<T> boolean putMapping(String indexName, String type, Class<T> clazz);
|
||||
|
||||
/**
|
||||
* Create mapping for a given indexName and type
|
||||
*
|
||||
@@ -101,14 +131,13 @@ public interface ElasticsearchOperations {
|
||||
*/
|
||||
<T> boolean putMapping(Class<T> clazz, Object mappings);
|
||||
|
||||
|
||||
/**
|
||||
* Get mapping for a class
|
||||
*
|
||||
* @param clazz
|
||||
* @param <T>
|
||||
*/
|
||||
<T> Map getMapping(Class<T> clazz);
|
||||
<T> Map<String, Object> getMapping(Class<T> clazz);
|
||||
|
||||
/**
|
||||
* Get mapping for a given indexName and type
|
||||
@@ -116,22 +145,31 @@ public interface ElasticsearchOperations {
|
||||
* @param indexName
|
||||
* @param type
|
||||
*/
|
||||
Map getMapping(String indexName, String type);
|
||||
Map<String, Object> getMapping(String indexName, String type);
|
||||
|
||||
/**
|
||||
* Get settings for a given indexName
|
||||
*
|
||||
* @param indexName
|
||||
*/
|
||||
Map getSetting(String indexName);
|
||||
Map<String, Object> getSetting(String indexName);
|
||||
|
||||
/**
|
||||
* Get settings for a given class
|
||||
*
|
||||
* @param clazz
|
||||
*/
|
||||
<T> Map getSetting(Class<T> clazz);
|
||||
<T> Map<String, Object> getSetting(Class<T> clazz);
|
||||
|
||||
/**
|
||||
* get all the alias pointing to specified index
|
||||
*
|
||||
* @param indexName
|
||||
* @return
|
||||
*/
|
||||
List<AliasMetaData> queryForAlias(String indexName);
|
||||
|
||||
<T> T query(SearchQuery query, ResultsExtractor<T> resultsExtractor);
|
||||
|
||||
/**
|
||||
* Execute the query against elasticsearch and return the first returned object
|
||||
@@ -198,7 +236,8 @@ public interface ElasticsearchOperations {
|
||||
<T> List<Page<T>> queryForPage(List<SearchQuery> queries, Class<T> clazz);
|
||||
|
||||
/**
|
||||
* Execute the multi-search against elasticsearch and return result as {@link List} of {@link Page} using custom mapper
|
||||
* Execute the multi-search against elasticsearch and return result as {@link List} of {@link Page} using custom
|
||||
* mapper
|
||||
*
|
||||
* @param queries
|
||||
* @param clazz
|
||||
@@ -216,7 +255,8 @@ public interface ElasticsearchOperations {
|
||||
List<Page<?>> queryForPage(List<SearchQuery> queries, List<Class<?>> classes);
|
||||
|
||||
/**
|
||||
* Execute the multi-search against elasticsearch and return result as {@link List} of {@link Page} using custom mapper
|
||||
* Execute the multi-search against elasticsearch and return result as {@link List} of {@link Page} using custom
|
||||
* mapper
|
||||
*
|
||||
* @param queries
|
||||
* @param classes
|
||||
@@ -254,7 +294,8 @@ public interface ElasticsearchOperations {
|
||||
/**
|
||||
* 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.
|
||||
* 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
|
||||
@@ -267,7 +308,8 @@ public interface ElasticsearchOperations {
|
||||
/**
|
||||
* 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.
|
||||
* 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
|
||||
@@ -278,9 +320,11 @@ public interface ElasticsearchOperations {
|
||||
<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.
|
||||
* 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.
|
||||
* 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
|
||||
@@ -393,7 +437,7 @@ public interface ElasticsearchOperations {
|
||||
* @param clazz
|
||||
* @return
|
||||
*/
|
||||
<T> LinkedList<T> multiGet(SearchQuery searchQuery, Class<T> clazz);
|
||||
<T> List<T> multiGet(SearchQuery searchQuery, Class<T> clazz);
|
||||
|
||||
/**
|
||||
* Execute a multiGet against elasticsearch for the given ids with MultiGetResultMapper
|
||||
@@ -403,7 +447,7 @@ public interface ElasticsearchOperations {
|
||||
* @param multiGetResultMapper
|
||||
* @return
|
||||
*/
|
||||
<T> LinkedList<T> multiGet(SearchQuery searchQuery, Class<T> clazz, MultiGetResultMapper multiGetResultMapper);
|
||||
<T> List<T> multiGet(SearchQuery searchQuery, Class<T> clazz, MultiGetResultMapper multiGetResultMapper);
|
||||
|
||||
/**
|
||||
* Index an object. Will do save or update
|
||||
@@ -422,18 +466,40 @@ public interface ElasticsearchOperations {
|
||||
UpdateResponse update(UpdateQuery updateQuery);
|
||||
|
||||
/**
|
||||
* Bulk index all objects. Will do save or update
|
||||
* Bulk index all objects. Will do save or update.
|
||||
*
|
||||
* @param queries
|
||||
* @param queries the queries to execute in bulk
|
||||
*/
|
||||
void bulkIndex(List<IndexQuery> queries);
|
||||
default void bulkIndex(List<IndexQuery> queries) {
|
||||
bulkIndex(queries, BulkOptions.defaultOptions());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @since 3.2
|
||||
*/
|
||||
void bulkIndex(List<IndexQuery> queries, BulkOptions bulkOptions);
|
||||
|
||||
/**
|
||||
* Bulk update all objects. Will do update
|
||||
*
|
||||
* @param queries
|
||||
* @param queries the queries to execute in bulk
|
||||
*/
|
||||
void bulkUpdate(List<UpdateQuery> queries);
|
||||
default void bulkUpdate(List<UpdateQuery> queries) {
|
||||
bulkUpdate(queries, BulkOptions.defaultOptions());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @since 3.2
|
||||
*/
|
||||
void bulkUpdate(List<UpdateQuery> queries, BulkOptions bulkOptions);
|
||||
|
||||
/**
|
||||
* Delete the one object with provided id
|
||||
@@ -445,7 +511,6 @@ public interface ElasticsearchOperations {
|
||||
*/
|
||||
String delete(String indexName, String type, String id);
|
||||
|
||||
|
||||
/**
|
||||
* Delete all records matching the criteria
|
||||
*
|
||||
@@ -453,6 +518,7 @@ public interface ElasticsearchOperations {
|
||||
* @param criteriaQuery
|
||||
*/
|
||||
<T> void delete(CriteriaQuery criteriaQuery, Class<T> clazz);
|
||||
|
||||
/**
|
||||
* Delete the one object with provided id
|
||||
*
|
||||
@@ -524,7 +590,6 @@ public interface ElasticsearchOperations {
|
||||
* refresh the index
|
||||
*
|
||||
* @param indexName
|
||||
*
|
||||
*/
|
||||
void refresh(String indexName);
|
||||
|
||||
@@ -532,7 +597,6 @@ public interface ElasticsearchOperations {
|
||||
* refresh the index
|
||||
*
|
||||
* @param clazz
|
||||
*
|
||||
*/
|
||||
<T> void refresh(Class<T> clazz);
|
||||
|
||||
@@ -541,7 +605,7 @@ public interface ElasticsearchOperations {
|
||||
*
|
||||
* @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)}.
|
||||
* {@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.
|
||||
*/
|
||||
@@ -552,18 +616,19 @@ public interface ElasticsearchOperations {
|
||||
*
|
||||
* @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)}.
|
||||
* {@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> ScrolledPage<T> startScroll(long scrollTimeInMillis, SearchQuery query, Class<T> clazz, SearchResultMapper mapper);
|
||||
<T> ScrolledPage<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)}.
|
||||
* {@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.
|
||||
*/
|
||||
@@ -574,20 +639,22 @@ public interface ElasticsearchOperations {
|
||||
*
|
||||
* @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)}.
|
||||
* {@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> ScrolledPage<T> startScroll(long scrollTimeInMillis, CriteriaQuery criteriaQuery, Class<T> clazz, SearchResultMapper mapper);
|
||||
|
||||
<T> ScrolledPage<T> startScroll(long scrollTimeInMillis, CriteriaQuery criteriaQuery, Class<T> clazz,
|
||||
SearchResultMapper mapper);
|
||||
|
||||
<T> ScrolledPage<T> continueScroll(@Nullable String scrollId, long scrollTimeInMillis, Class<T> clazz);
|
||||
<T> ScrolledPage<T> continueScroll(@Nullable String scrollId, long scrollTimeInMillis, Class<T> clazz, SearchResultMapper mapper);
|
||||
|
||||
<T> ScrolledPage<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);
|
||||
|
||||
@@ -601,33 +668,10 @@ public interface ElasticsearchOperations {
|
||||
*/
|
||||
<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);
|
||||
|
||||
/**
|
||||
* @return Converter in use
|
||||
*/
|
||||
ElasticsearchConverter getElasticsearchConverter();
|
||||
}
|
||||
|
||||
+258
-230
@@ -20,16 +20,13 @@ import static org.elasticsearch.index.query.QueryBuilders.*;
|
||||
import static org.springframework.util.CollectionUtils.isEmpty;
|
||||
import static org.springframework.util.StringUtils.*;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
@@ -65,7 +62,6 @@ import org.elasticsearch.client.Response;
|
||||
import org.elasticsearch.client.RestClient;
|
||||
import org.elasticsearch.client.RestHighLevelClient;
|
||||
import org.elasticsearch.cluster.metadata.AliasMetaData;
|
||||
import org.elasticsearch.common.Nullable;
|
||||
import org.elasticsearch.common.collect.MapBuilder;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
@@ -83,23 +79,21 @@ import org.elasticsearch.search.aggregations.AbstractAggregationBuilder;
|
||||
import org.elasticsearch.search.builder.SearchSourceBuilder;
|
||||
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
|
||||
import org.elasticsearch.search.sort.FieldSortBuilder;
|
||||
import org.elasticsearch.search.sort.ScoreSortBuilder;
|
||||
import org.elasticsearch.search.sort.SortBuilder;
|
||||
import org.elasticsearch.search.sort.SortBuilders;
|
||||
import org.elasticsearch.search.sort.SortOrder;
|
||||
import org.elasticsearch.search.suggest.SuggestBuilder;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.elasticsearch.ElasticsearchException;
|
||||
import org.springframework.data.elasticsearch.annotations.Document;
|
||||
import org.springframework.data.elasticsearch.annotations.Mapping;
|
||||
import org.springframework.data.elasticsearch.annotations.Setting;
|
||||
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
|
||||
import org.springframework.data.elasticsearch.core.client.support.AliasData;
|
||||
@@ -111,6 +105,7 @@ import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersiste
|
||||
import org.springframework.data.elasticsearch.core.mapping.SimpleElasticsearchMappingContext;
|
||||
import org.springframework.data.elasticsearch.core.query.*;
|
||||
import org.springframework.data.util.CloseableIterator;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@@ -142,13 +137,19 @@ import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
* @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
|
||||
*/
|
||||
public class ElasticsearchRestTemplate
|
||||
public class ElasticsearchRestTemplate extends AbstractElasticsearchTemplate
|
||||
implements ElasticsearchOperations, EsClient<RestHighLevelClient>, ApplicationContextAware {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(ElasticsearchRestTemplate.class);
|
||||
|
||||
private RestHighLevelClient client;
|
||||
private ElasticsearchConverter elasticsearchConverter;
|
||||
private ResultsMapper resultsMapper;
|
||||
private String searchTimeout;
|
||||
|
||||
@@ -177,8 +178,9 @@ public class ElasticsearchRestTemplate
|
||||
public ElasticsearchRestTemplate(RestHighLevelClient client, ElasticsearchConverter elasticsearchConverter,
|
||||
ResultsMapper resultsMapper) {
|
||||
|
||||
super(elasticsearchConverter);
|
||||
|
||||
Assert.notNull(client, "Client must not be null!");
|
||||
Assert.notNull(elasticsearchConverter, "ElasticsearchConverter must not be null!");
|
||||
Assert.notNull(resultsMapper, "ResultsMapper must not be null!");
|
||||
|
||||
this.client = client;
|
||||
@@ -195,6 +197,54 @@ public class ElasticsearchRestTemplate
|
||||
this.searchTimeout = searchTimeout;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAlias(AliasQuery query) {
|
||||
Assert.notNull(query.getIndexName(), "No index defined for Alias");
|
||||
Assert.notNull(query.getAliasName(), "No alias defined");
|
||||
IndicesAliasesRequest.AliasActions aliasAction = IndicesAliasesRequest.AliasActions.add()
|
||||
.alias(query.getAliasName()).index(query.getIndexName());
|
||||
|
||||
if (query.getFilterBuilder() != null) {
|
||||
aliasAction.filter(query.getFilterBuilder());
|
||||
} else if (query.getFilter() != null) {
|
||||
aliasAction.filter(query.getFilter());
|
||||
} else if (hasText(query.getRouting())) {
|
||||
aliasAction.routing(query.getRouting());
|
||||
} else if (hasText(query.getSearchRouting())) {
|
||||
aliasAction.searchRouting(query.getSearchRouting());
|
||||
} else if (hasText(query.getIndexRouting())) {
|
||||
aliasAction.indexRouting(query.getIndexRouting());
|
||||
}
|
||||
|
||||
IndicesAliasesRequest request = new IndicesAliasesRequest();
|
||||
request.addAliasAction(aliasAction);
|
||||
try {
|
||||
return client.indices().updateAliases(request, RequestOptions.DEFAULT).isAcknowledged();
|
||||
} catch (IOException e) {
|
||||
throw new ElasticsearchException("failed to update aliases with request: " + request, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeAlias(AliasQuery query) {
|
||||
|
||||
Assert.notNull(query.getIndexName(), "No index defined for Alias");
|
||||
Assert.notNull(query.getAliasName(), "No alias defined");
|
||||
|
||||
AliasActions aliasAction = IndicesAliasesRequest.AliasActions.remove() //
|
||||
.index(query.getIndexName()) //
|
||||
.alias(query.getAliasName());
|
||||
|
||||
IndicesAliasesRequest request = Requests.indexAliasesRequest() //
|
||||
.addAliasAction(aliasAction);
|
||||
|
||||
try {
|
||||
return client.indices().updateAliases(request, RequestOptions.DEFAULT).isAcknowledged();
|
||||
} catch (IOException e) {
|
||||
throw new ElasticsearchException("failed to update aliases with request: " + request, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> boolean createIndex(Class<T> clazz) {
|
||||
return createIndexIfNotCreated(clazz);
|
||||
@@ -204,7 +254,7 @@ public class ElasticsearchRestTemplate
|
||||
public boolean createIndex(String indexName) {
|
||||
Assert.notNull(indexName, "No index defined for Query");
|
||||
try {
|
||||
return client.indices().create(Requests.createIndexRequest(indexName)).isAcknowledged();
|
||||
return client.indices().create(createIndexRequest(indexName), RequestOptions.DEFAULT).isAcknowledged();
|
||||
} catch (Exception e) {
|
||||
throw new ElasticsearchException("Failed to create index " + indexName, e);
|
||||
}
|
||||
@@ -212,23 +262,7 @@ public class ElasticsearchRestTemplate
|
||||
|
||||
@Override
|
||||
public <T> boolean putMapping(Class<T> clazz) {
|
||||
if (clazz.isAnnotationPresent(Mapping.class)) {
|
||||
String mappingPath = clazz.getAnnotation(Mapping.class).mappingPath();
|
||||
if (hasText(mappingPath)) {
|
||||
String mappings = readFileFromClasspath(mappingPath);
|
||||
if (hasText(mappings)) {
|
||||
return putMapping(clazz, mappings);
|
||||
}
|
||||
} else {
|
||||
logger.info("mappingPath in @Mapping has to be defined. Building mappings using @Field");
|
||||
}
|
||||
}
|
||||
try {
|
||||
MappingBuilder mappingBuilder = new MappingBuilder(elasticsearchConverter);
|
||||
return putMapping(clazz, mappingBuilder.buildPropertyMapping(clazz));
|
||||
} catch (Exception e) {
|
||||
throw new ElasticsearchException("Failed to build mapping for " + clazz.getSimpleName(), e);
|
||||
}
|
||||
return putMapping(clazz, buildMapping(clazz));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -237,6 +271,11 @@ public class ElasticsearchRestTemplate
|
||||
mapping);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> boolean putMapping(String indexName, String type, Class<T> clazz) {
|
||||
return putMapping(indexName, type, buildMapping(clazz));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean putMapping(String indexName, String type, Object mapping) {
|
||||
Assert.notNull(indexName, "No index defined for putMapping()");
|
||||
@@ -250,17 +289,17 @@ public class ElasticsearchRestTemplate
|
||||
request.source((XContentBuilder) mapping);
|
||||
}
|
||||
try {
|
||||
return client.indices().putMapping(request).isAcknowledged();
|
||||
return client.indices().putMapping(request, RequestOptions.DEFAULT).isAcknowledged();
|
||||
} catch (IOException e) {
|
||||
throw new ElasticsearchException("Failed to put mapping for " + indexName, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map getMapping(String indexName, String type) {
|
||||
public Map<String, Object> getMapping(String indexName, String type) {
|
||||
Assert.notNull(indexName, "No index defined for getMapping()");
|
||||
Assert.notNull(type, "No type defined for getMapping()");
|
||||
Map mappings = null;
|
||||
Map<String, Object> mappings = null;
|
||||
RestClient restClient = client.getLowLevelClient();
|
||||
try {
|
||||
Response response = restClient.performRequest("GET", "/" + indexName + "/_mapping/" + type);
|
||||
@@ -273,7 +312,7 @@ public class ElasticsearchRestTemplate
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Map getMapping(Class<T> clazz) {
|
||||
public <T> Map<String, Object> getMapping(Class<T> clazz) {
|
||||
return getMapping(getPersistentEntityFor(clazz).getIndexName(), getPersistentEntityFor(clazz).getIndexType());
|
||||
}
|
||||
|
||||
@@ -281,7 +320,7 @@ public class ElasticsearchRestTemplate
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
|
||||
try {
|
||||
Map result = null;
|
||||
Map<String, Object> result = null;
|
||||
JsonNode node = mapper.readTree(mappingResponse);
|
||||
|
||||
node = node.findValue("mappings").findValue(type);
|
||||
@@ -311,9 +350,8 @@ public class ElasticsearchRestTemplate
|
||||
query.getId());
|
||||
GetResponse response;
|
||||
try {
|
||||
response = client.get(request);
|
||||
T entity = mapper.mapResult(response, clazz);
|
||||
return entity;
|
||||
response = client.get(request, RequestOptions.DEFAULT);
|
||||
return mapper.mapResult(response, clazz);
|
||||
} catch (IOException e) {
|
||||
throw new ElasticsearchException("Error while getting for request: " + request.toString(), e);
|
||||
}
|
||||
@@ -375,7 +413,7 @@ public class ElasticsearchRestTemplate
|
||||
private MultiSearchResponse.Item[] getMultiSearchResult(MultiSearchRequest request) {
|
||||
MultiSearchResponse response;
|
||||
try {
|
||||
response = client.multiSearch(request);
|
||||
response = client.multiSearch(request, RequestOptions.DEFAULT);
|
||||
} catch (IOException e) {
|
||||
throw new ElasticsearchException("Error for search request: " + request.toString(), e);
|
||||
}
|
||||
@@ -410,7 +448,7 @@ public class ElasticsearchRestTemplate
|
||||
|
||||
@Override
|
||||
public <T> T query(SearchQuery query, ResultsExtractor<T> resultsExtractor) {
|
||||
SearchResponse response = doSearch(prepareSearch(query, Optional.ofNullable(query.getQuery())), query);
|
||||
SearchResponse response = doSearch(prepareSearch(query, Optional.ofNullable(query.getQuery()), null), query);
|
||||
return resultsExtractor.extract(response);
|
||||
}
|
||||
|
||||
@@ -431,14 +469,14 @@ public class ElasticsearchRestTemplate
|
||||
|
||||
@Override
|
||||
public <T> List<String> queryForIds(SearchQuery query) {
|
||||
SearchRequest request = prepareSearch(query, Optional.ofNullable(query.getQuery()));
|
||||
SearchRequest request = prepareSearch(query, Optional.ofNullable(query.getQuery()), null);
|
||||
request.source().query(query.getQuery());
|
||||
if (query.getFilter() != null) {
|
||||
request.source().postFilter(query.getFilter());
|
||||
}
|
||||
SearchResponse response;
|
||||
try {
|
||||
response = client.search(request);
|
||||
response = client.search(request, RequestOptions.DEFAULT);
|
||||
} catch (IOException e) {
|
||||
throw new ElasticsearchException("Error for search request: " + request.toString(), e);
|
||||
}
|
||||
@@ -470,7 +508,7 @@ public class ElasticsearchRestTemplate
|
||||
|
||||
SearchResponse response;
|
||||
try {
|
||||
response = client.search(request);
|
||||
response = client.search(request, RequestOptions.DEFAULT);
|
||||
} catch (IOException e) {
|
||||
throw new ElasticsearchException("Error for search request: " + request.toString(), e);
|
||||
}
|
||||
@@ -488,7 +526,7 @@ public class ElasticsearchRestTemplate
|
||||
request.source().query((wrapperQuery(query.getSource())));
|
||||
SearchResponse response;
|
||||
try {
|
||||
response = client.search(request);
|
||||
response = client.search(request, RequestOptions.DEFAULT);
|
||||
} catch (IOException e) {
|
||||
throw new ElasticsearchException("Error for search request: " + request.toString(), e);
|
||||
}
|
||||
@@ -497,7 +535,7 @@ public class ElasticsearchRestTemplate
|
||||
|
||||
@Override
|
||||
public <T> CloseableIterator<T> stream(CriteriaQuery query, Class<T> clazz) {
|
||||
final long scrollTimeInMillis = TimeValue.timeValueMinutes(1).millis();
|
||||
long scrollTimeInMillis = TimeValue.timeValueMinutes(1).millis();
|
||||
return doStream(scrollTimeInMillis, startScroll(scrollTimeInMillis, query, clazz), clazz, resultsMapper);
|
||||
}
|
||||
|
||||
@@ -507,68 +545,15 @@ public class ElasticsearchRestTemplate
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> CloseableIterator<T> stream(SearchQuery query, final Class<T> clazz, final SearchResultMapper mapper) {
|
||||
final long scrollTimeInMillis = TimeValue.timeValueMinutes(1).millis();
|
||||
public <T> CloseableIterator<T> stream(SearchQuery query, Class<T> clazz, SearchResultMapper mapper) {
|
||||
long scrollTimeInMillis = TimeValue.timeValueMinutes(1).millis();
|
||||
return doStream(scrollTimeInMillis, startScroll(scrollTimeInMillis, query, clazz, mapper), clazz, mapper);
|
||||
}
|
||||
|
||||
private <T> CloseableIterator<T> doStream(final long scrollTimeInMillis, final ScrolledPage<T> page,
|
||||
final Class<T> clazz, final SearchResultMapper mapper) {
|
||||
return new CloseableIterator<T>() {
|
||||
|
||||
/** As we couldn't retrieve single result with scroll, store current hits. */
|
||||
private volatile Iterator<T> currentHits = page.iterator();
|
||||
|
||||
/** The scroll id. */
|
||||
private volatile String scrollId = page.getScrollId();
|
||||
|
||||
/** If stream is finished (ie: cluster returns no results. */
|
||||
private volatile boolean finished = !currentHits.hasNext();
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
try {
|
||||
// Clear scroll on cluster only in case of error (cause elasticsearch auto clear scroll when it's done)
|
||||
if (!finished && scrollId != null && currentHits != null && currentHits.hasNext()) {
|
||||
clearScroll(scrollId);
|
||||
}
|
||||
} finally {
|
||||
currentHits = null;
|
||||
scrollId = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
// Test if stream is finished
|
||||
if (finished) {
|
||||
return false;
|
||||
}
|
||||
// Test if it remains hits
|
||||
if (currentHits == null || !currentHits.hasNext()) {
|
||||
// Do a new request
|
||||
final ScrolledPage<T> scroll = continueScroll(scrollId, scrollTimeInMillis, clazz, mapper);
|
||||
// Save hits and scroll id
|
||||
currentHits = scroll.iterator();
|
||||
finished = !currentHits.hasNext();
|
||||
scrollId = scroll.getScrollId();
|
||||
}
|
||||
return currentHits.hasNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public T next() {
|
||||
if (hasNext()) {
|
||||
return currentHits.next();
|
||||
}
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException("remove");
|
||||
}
|
||||
};
|
||||
private <T> CloseableIterator<T> doStream(long scrollTimeInMillis, ScrolledPage<T> page, Class<T> clazz,
|
||||
SearchResultMapper mapper) {
|
||||
return StreamQueries.streamResults(page, scrollId -> continueScroll(scrollId, scrollTimeInMillis, clazz, mapper),
|
||||
this::clearScroll);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -613,10 +598,11 @@ public class ElasticsearchRestTemplate
|
||||
if (elasticsearchQuery != null) {
|
||||
sourceBuilder.query(elasticsearchQuery);
|
||||
}
|
||||
sourceBuilder.size(0);
|
||||
countRequest.source(sourceBuilder);
|
||||
|
||||
try {
|
||||
return client.search(countRequest).getHits().getTotalHits();
|
||||
return client.search(countRequest, RequestOptions.DEFAULT).getHits().getTotalHits();
|
||||
} catch (IOException e) {
|
||||
throw new ElasticsearchException("Error while searching for request: " + countRequest.toString(), e);
|
||||
}
|
||||
@@ -631,9 +617,10 @@ public class ElasticsearchRestTemplate
|
||||
if (elasticsearchFilter != null) {
|
||||
searchRequest.source().postFilter(elasticsearchFilter);
|
||||
}
|
||||
searchRequest.source().size(0);
|
||||
SearchResponse response;
|
||||
try {
|
||||
response = client.search(searchRequest);
|
||||
response = client.search(searchRequest, RequestOptions.DEFAULT);
|
||||
} catch (IOException e) {
|
||||
throw new ElasticsearchException("Error for search request: " + searchRequest.toString(), e);
|
||||
}
|
||||
@@ -641,10 +628,10 @@ public class ElasticsearchRestTemplate
|
||||
}
|
||||
|
||||
private <T> SearchRequest prepareCount(Query query, Class<T> clazz) {
|
||||
String indexName[] = !isEmpty(query.getIndices())
|
||||
String[] indexName = !isEmpty(query.getIndices())
|
||||
? query.getIndices().toArray(new String[query.getIndices().size()])
|
||||
: retrieveIndexNameFromPersistentEntity(clazz);
|
||||
String types[] = !isEmpty(query.getTypes()) ? query.getTypes().toArray(new String[query.getTypes().size()])
|
||||
String[] types = !isEmpty(query.getTypes()) ? query.getTypes().toArray(new String[query.getTypes().size()])
|
||||
: retrieveTypeFromPersistentEntity(clazz);
|
||||
|
||||
Assert.notNull(indexName, "No index defined for Query");
|
||||
@@ -658,7 +645,7 @@ public class ElasticsearchRestTemplate
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> LinkedList<T> multiGet(SearchQuery searchQuery, Class<T> clazz) {
|
||||
public <T> List<T> multiGet(SearchQuery searchQuery, Class<T> clazz) {
|
||||
return resultsMapper.mapResults(getMultiResponse(searchQuery, clazz), clazz);
|
||||
}
|
||||
|
||||
@@ -690,14 +677,14 @@ public class ElasticsearchRestTemplate
|
||||
request.add(item);
|
||||
}
|
||||
try {
|
||||
return client.multiGet(request);
|
||||
return client.multiGet(request, RequestOptions.DEFAULT);
|
||||
} catch (IOException e) {
|
||||
throw new ElasticsearchException("Error while multiget for request: " + request.toString(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> LinkedList<T> multiGet(SearchQuery searchQuery, Class<T> clazz, MultiGetResultMapper getResultMapper) {
|
||||
public <T> List<T> multiGet(SearchQuery searchQuery, Class<T> clazz, MultiGetResultMapper getResultMapper) {
|
||||
return getResultMapper.mapResults(getMultiResponse(searchQuery, clazz), clazz);
|
||||
}
|
||||
|
||||
@@ -706,7 +693,7 @@ public class ElasticsearchRestTemplate
|
||||
String documentId;
|
||||
IndexRequest request = prepareIndex(query);
|
||||
try {
|
||||
documentId = client.index(request).getId();
|
||||
documentId = client.index(request, RequestOptions.DEFAULT).getId();
|
||||
} catch (IOException e) {
|
||||
throw new ElasticsearchException("Error while index for request: " + request.toString(), e);
|
||||
}
|
||||
@@ -721,64 +708,110 @@ public class ElasticsearchRestTemplate
|
||||
public UpdateResponse update(UpdateQuery query) {
|
||||
UpdateRequest request = prepareUpdate(query);
|
||||
try {
|
||||
return client.update(request);
|
||||
return client.update(request, RequestOptions.DEFAULT);
|
||||
} catch (IOException e) {
|
||||
throw new ElasticsearchException("Error while update for request: " + request.toString(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private UpdateRequest prepareUpdate(UpdateQuery query) {
|
||||
|
||||
String indexName = hasText(query.getIndexName()) ? query.getIndexName()
|
||||
: getPersistentEntityFor(query.getClazz()).getIndexName();
|
||||
String type = hasText(query.getType()) ? query.getType() : getPersistentEntityFor(query.getClazz()).getIndexType();
|
||||
|
||||
Assert.notNull(indexName, "No index defined for Query");
|
||||
Assert.notNull(type, "No type define for Query");
|
||||
Assert.notNull(query.getId(), "No Id define for Query");
|
||||
Assert.notNull(query.getUpdateRequest(), "No IndexRequest define for Query");
|
||||
UpdateRequest updateRequest = new UpdateRequest(indexName, type, query.getId());
|
||||
updateRequest.routing(query.getUpdateRequest().routing());
|
||||
Assert.notNull(query.getUpdateRequest(), "No UpdateRequest define for Query");
|
||||
|
||||
if (query.getUpdateRequest().script() == null) {
|
||||
// doc
|
||||
if (query.DoUpsert()) {
|
||||
updateRequest.docAsUpsert(true).doc(query.getUpdateRequest().doc());
|
||||
} else {
|
||||
updateRequest.doc(query.getUpdateRequest().doc());
|
||||
}
|
||||
} else {
|
||||
// or script
|
||||
updateRequest.script(query.getUpdateRequest().script());
|
||||
UpdateRequest queryUpdateRequest = query.getUpdateRequest();
|
||||
|
||||
UpdateRequest updateRequest = new UpdateRequest(indexName, type, query.getId()) //
|
||||
.routing(queryUpdateRequest.routing()) //
|
||||
.retryOnConflict(queryUpdateRequest.retryOnConflict()) //
|
||||
.timeout(queryUpdateRequest.timeout()) //
|
||||
.waitForActiveShards(queryUpdateRequest.waitForActiveShards()) //
|
||||
.setRefreshPolicy(queryUpdateRequest.getRefreshPolicy()) //
|
||||
.waitForActiveShards(queryUpdateRequest.waitForActiveShards()) //
|
||||
.scriptedUpsert(queryUpdateRequest.scriptedUpsert()) //
|
||||
.docAsUpsert(queryUpdateRequest.docAsUpsert());
|
||||
|
||||
if (query.DoUpsert()) {
|
||||
updateRequest.docAsUpsert(true);
|
||||
}
|
||||
if (queryUpdateRequest.script() != null) {
|
||||
updateRequest.script(queryUpdateRequest.script());
|
||||
}
|
||||
if (queryUpdateRequest.doc() != null) {
|
||||
updateRequest.doc(queryUpdateRequest.doc());
|
||||
}
|
||||
if (queryUpdateRequest.upsertRequest() != null) {
|
||||
updateRequest.upsert(queryUpdateRequest.upsertRequest());
|
||||
}
|
||||
|
||||
return updateRequest;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bulkIndex(List<IndexQuery> queries) {
|
||||
public void bulkIndex(List<IndexQuery> queries, BulkOptions bulkOptions) {
|
||||
|
||||
Assert.notNull(queries, "List of IndexQuery must not be null");
|
||||
Assert.notNull(bulkOptions, "BulkOptions must not be null");
|
||||
|
||||
BulkRequest bulkRequest = new BulkRequest();
|
||||
setBulkOptions(bulkRequest, bulkOptions);
|
||||
for (IndexQuery query : queries) {
|
||||
bulkRequest.add(prepareIndex(query));
|
||||
}
|
||||
try {
|
||||
checkForBulkUpdateFailure(client.bulk(bulkRequest));
|
||||
checkForBulkUpdateFailure(client.bulk(bulkRequest, RequestOptions.DEFAULT));
|
||||
} catch (IOException e) {
|
||||
throw new ElasticsearchException("Error while bulk for request: " + bulkRequest.toString(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bulkUpdate(List<UpdateQuery> queries) {
|
||||
public void bulkUpdate(List<UpdateQuery> queries, BulkOptions bulkOptions) {
|
||||
|
||||
Assert.notNull(queries, "List of UpdateQuery must not be null");
|
||||
Assert.notNull(bulkOptions, "BulkOptions must not be null");
|
||||
|
||||
BulkRequest bulkRequest = new BulkRequest();
|
||||
setBulkOptions(bulkRequest, bulkOptions);
|
||||
for (UpdateQuery query : queries) {
|
||||
bulkRequest.add(prepareUpdate(query));
|
||||
}
|
||||
try {
|
||||
checkForBulkUpdateFailure(client.bulk(bulkRequest));
|
||||
checkForBulkUpdateFailure(client.bulk(bulkRequest, RequestOptions.DEFAULT));
|
||||
} catch (IOException e) {
|
||||
throw new ElasticsearchException("Error while bulk for request: " + bulkRequest.toString(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void setBulkOptions(BulkRequest bulkRequest, BulkOptions bulkOptions) {
|
||||
|
||||
if (bulkOptions.getTimeout() != null) {
|
||||
bulkRequest.timeout(bulkOptions.getTimeout());
|
||||
}
|
||||
|
||||
if (bulkOptions.getRefreshPolicy() != null) {
|
||||
bulkRequest.setRefreshPolicy(bulkOptions.getRefreshPolicy());
|
||||
}
|
||||
|
||||
if (bulkOptions.getWaitForActiveShards() != null) {
|
||||
bulkRequest.waitForActiveShards(bulkOptions.getWaitForActiveShards());
|
||||
}
|
||||
|
||||
if (bulkOptions.getPipeline() != null) {
|
||||
bulkRequest.pipeline(bulkOptions.getPipeline());
|
||||
}
|
||||
|
||||
if (bulkOptions.getRoutingId() != null) {
|
||||
bulkRequest.routing(bulkOptions.getRoutingId());
|
||||
}
|
||||
}
|
||||
|
||||
private void checkForBulkUpdateFailure(BulkResponse bulkResponse) {
|
||||
if (bulkResponse.hasFailures()) {
|
||||
Map<String, String> failedDocuments = new HashMap<>();
|
||||
@@ -803,7 +836,7 @@ public class ElasticsearchRestTemplate
|
||||
GetIndexRequest request = new GetIndexRequest();
|
||||
request.indices(indexName);
|
||||
try {
|
||||
return client.indices().exists(request);
|
||||
return client.indices().exists(request, RequestOptions.DEFAULT);
|
||||
} catch (IOException e) {
|
||||
throw new ElasticsearchException("Error while for indexExists request: " + request.toString(), e);
|
||||
}
|
||||
@@ -832,7 +865,7 @@ public class ElasticsearchRestTemplate
|
||||
if (indexExists(indexName)) {
|
||||
DeleteIndexRequest request = new DeleteIndexRequest(indexName);
|
||||
try {
|
||||
return client.indices().delete(request).isAcknowledged();
|
||||
return client.indices().delete(request, RequestOptions.DEFAULT).isAcknowledged();
|
||||
} catch (IOException e) {
|
||||
throw new ElasticsearchException("Error while deleting index request: " + request.toString(), e);
|
||||
}
|
||||
@@ -844,7 +877,7 @@ public class ElasticsearchRestTemplate
|
||||
public String delete(String indexName, String type, String id) {
|
||||
DeleteRequest request = new DeleteRequest(indexName, type, id);
|
||||
try {
|
||||
return client.delete(request).getId();
|
||||
return client.delete(request, RequestOptions.DEFAULT).getId();
|
||||
} catch (IOException e) {
|
||||
throw new ElasticsearchException("Error while deleting item request: " + request.toString(), e);
|
||||
}
|
||||
@@ -901,10 +934,12 @@ public class ElasticsearchRestTemplate
|
||||
|
||||
private <T> SearchRequest prepareScroll(Query query, long scrollTimeInMillis, Class<T> clazz) {
|
||||
setPersistentEntityIndexAndType(query, clazz);
|
||||
return prepareScroll(query, scrollTimeInMillis);
|
||||
ElasticsearchPersistentEntity<?> entity = getPersistentEntity(clazz);
|
||||
return prepareScroll(query, scrollTimeInMillis, entity);
|
||||
}
|
||||
|
||||
private SearchRequest prepareScroll(Query query, long scrollTimeInMillis) {
|
||||
private SearchRequest prepareScroll(Query query, long scrollTimeInMillis,
|
||||
@Nullable ElasticsearchPersistentEntity<?> entity) {
|
||||
SearchRequest request = new SearchRequest(toArray(query.getIndices()));
|
||||
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
|
||||
request.types(toArray(query.getTypes()));
|
||||
@@ -924,7 +959,28 @@ public class ElasticsearchRestTemplate
|
||||
}
|
||||
|
||||
if (query.getSort() != null) {
|
||||
prepareSort(query, searchSourceBuilder);
|
||||
prepareSort(query, searchSourceBuilder, entity);
|
||||
}
|
||||
|
||||
if (query.getIndicesOptions() != null) {
|
||||
request.indicesOptions(query.getIndicesOptions());
|
||||
}
|
||||
|
||||
if (query instanceof SearchQuery) {
|
||||
SearchQuery searchQuery = (SearchQuery) query;
|
||||
|
||||
if (searchQuery.getHighlightFields() != null || searchQuery.getHighlightBuilder() != null) {
|
||||
HighlightBuilder highlightBuilder = searchQuery.getHighlightBuilder();
|
||||
if (highlightBuilder == null) {
|
||||
highlightBuilder = new HighlightBuilder();
|
||||
}
|
||||
if (searchQuery.getHighlightFields() != null) {
|
||||
for (HighlightBuilder.Field highlightField : searchQuery.getHighlightFields()) {
|
||||
highlightBuilder.field(highlightField);
|
||||
}
|
||||
}
|
||||
searchSourceBuilder.highlighter(highlightBuilder);
|
||||
}
|
||||
}
|
||||
|
||||
request.source(searchSourceBuilder);
|
||||
@@ -952,7 +1008,7 @@ public class ElasticsearchRestTemplate
|
||||
request.source().version(true);
|
||||
|
||||
try {
|
||||
return client.search(request);
|
||||
return client.search(request, RequestOptions.DEFAULT);
|
||||
} catch (IOException e) {
|
||||
throw new ElasticsearchException("Error for search request with scroll: " + request.toString(), e);
|
||||
}
|
||||
@@ -981,7 +1037,7 @@ public class ElasticsearchRestTemplate
|
||||
}
|
||||
|
||||
try {
|
||||
return client.search(request);
|
||||
return client.search(request, RequestOptions.DEFAULT);
|
||||
} catch (IOException e) {
|
||||
throw new ElasticsearchException("Error for search request with scroll: " + request.toString(), e);
|
||||
}
|
||||
@@ -1014,7 +1070,7 @@ public class ElasticsearchRestTemplate
|
||||
request.scroll(TimeValue.timeValueMillis(scrollTimeInMillis));
|
||||
SearchResponse response;
|
||||
try {
|
||||
response = client.searchScroll(request);
|
||||
response = client.searchScroll(request, RequestOptions.DEFAULT);
|
||||
} catch (IOException e) {
|
||||
throw new ElasticsearchException("Error for search request with scroll: " + request.toString(), e);
|
||||
}
|
||||
@@ -1027,7 +1083,7 @@ public class ElasticsearchRestTemplate
|
||||
request.scroll(TimeValue.timeValueMillis(scrollTimeInMillis));
|
||||
SearchResponse response;
|
||||
try {
|
||||
response = client.searchScroll(request);
|
||||
response = client.searchScroll(request, RequestOptions.DEFAULT);
|
||||
} catch (IOException e) {
|
||||
throw new ElasticsearchException("Error for search request with scroll: " + request.toString(), e);
|
||||
}
|
||||
@@ -1040,7 +1096,7 @@ public class ElasticsearchRestTemplate
|
||||
request.addScrollId(scrollId);
|
||||
try {
|
||||
// TODO: Something useful with the response.
|
||||
ClearScrollResponse response = client.clearScroll(request);
|
||||
ClearScrollResponse response = client.clearScroll(request, RequestOptions.DEFAULT);
|
||||
} catch (IOException e) {
|
||||
throw new ElasticsearchException("Error for search request with scroll: " + request.toString(), e);
|
||||
}
|
||||
@@ -1092,7 +1148,7 @@ public class ElasticsearchRestTemplate
|
||||
prepareSearch(searchRequest, searchQuery);
|
||||
|
||||
try {
|
||||
return client.search(searchRequest);
|
||||
return client.search(searchRequest, RequestOptions.DEFAULT);
|
||||
} catch (IOException e) {
|
||||
throw new ElasticsearchException("Error for search request with scroll: " + searchRequest.toString(), e);
|
||||
}
|
||||
@@ -1117,6 +1173,10 @@ public class ElasticsearchRestTemplate
|
||||
}
|
||||
}
|
||||
|
||||
if (searchQuery.getCollapseBuilder() != null) {
|
||||
searchRequest.source().collapse(searchQuery.getCollapseBuilder());
|
||||
}
|
||||
|
||||
if (searchQuery.getHighlightFields() != null || searchQuery.getHighlightBuilder() != null) {
|
||||
HighlightBuilder highlightBuilder = searchQuery.getHighlightBuilder();
|
||||
if (highlightBuilder == null) {
|
||||
@@ -1162,7 +1222,7 @@ public class ElasticsearchRestTemplate
|
||||
if (clazz.isAnnotationPresent(Setting.class)) {
|
||||
String settingPath = clazz.getAnnotation(Setting.class).settingPath();
|
||||
if (hasText(settingPath)) {
|
||||
String settings = readFileFromClasspath(settingPath);
|
||||
String settings = ResourceUtil.readFileFromClasspath(settingPath);
|
||||
if (hasText(settings)) {
|
||||
return createIndex(getPersistentEntityFor(clazz).getIndexName(), settings);
|
||||
}
|
||||
@@ -1184,7 +1244,7 @@ public class ElasticsearchRestTemplate
|
||||
request.settings((XContentBuilder) settings);
|
||||
}
|
||||
try {
|
||||
return client.indices().create(request).isAcknowledged();
|
||||
return client.indices().create(request, RequestOptions.DEFAULT).isAcknowledged();
|
||||
} catch (IOException e) {
|
||||
throw new ElasticsearchException("Error for creating index: " + request.toString(), e);
|
||||
}
|
||||
@@ -1249,15 +1309,15 @@ public class ElasticsearchRestTemplate
|
||||
|
||||
private <T> SearchRequest prepareSearch(Query query, Class<T> clazz) {
|
||||
setPersistentEntityIndexAndType(query, clazz);
|
||||
return prepareSearch(query, Optional.empty());
|
||||
return prepareSearch(query, Optional.empty(), clazz);
|
||||
}
|
||||
|
||||
private <T> SearchRequest prepareSearch(SearchQuery query, Class<T> clazz) {
|
||||
setPersistentEntityIndexAndType(query, clazz);
|
||||
return prepareSearch(query, Optional.ofNullable(query.getQuery()));
|
||||
return prepareSearch(query, Optional.ofNullable(query.getQuery()), clazz);
|
||||
}
|
||||
|
||||
private SearchRequest prepareSearch(Query query, Optional<QueryBuilder> builder) {
|
||||
private SearchRequest prepareSearch(Query query, Optional<QueryBuilder> builder, @Nullable Class<?> clazz) {
|
||||
Assert.notNull(query.getIndices(), "No index defined for Query");
|
||||
Assert.notNull(query.getTypes(), "No type defined for Query");
|
||||
|
||||
@@ -1292,26 +1352,55 @@ public class ElasticsearchRestTemplate
|
||||
}
|
||||
|
||||
if (query.getSort() != null) {
|
||||
prepareSort(query, sourceBuilder);
|
||||
prepareSort(query, sourceBuilder, getPersistentEntity(clazz));
|
||||
}
|
||||
|
||||
if (query.getMinScore() > 0) {
|
||||
sourceBuilder.minScore(query.getMinScore());
|
||||
}
|
||||
|
||||
if (query.getPreference() != null) {
|
||||
request.preference(query.getPreference());
|
||||
}
|
||||
|
||||
if (query.getSearchType() != null) {
|
||||
request.searchType(query.getSearchType());
|
||||
}
|
||||
|
||||
request.source(sourceBuilder);
|
||||
return request;
|
||||
}
|
||||
|
||||
private void prepareSort(Query query, SearchSourceBuilder sourceBuilder) {
|
||||
private void prepareSort(Query query, SearchSourceBuilder sourceBuilder,
|
||||
@Nullable ElasticsearchPersistentEntity<?> entity) {
|
||||
|
||||
for (Sort.Order order : query.getSort()) {
|
||||
FieldSortBuilder sort = SortBuilders.fieldSort(order.getProperty())
|
||||
.order(order.getDirection().isDescending() ? SortOrder.DESC : SortOrder.ASC);
|
||||
if (order.getNullHandling() == Sort.NullHandling.NULLS_FIRST) {
|
||||
sort.missing("_first");
|
||||
} else if (order.getNullHandling() == Sort.NullHandling.NULLS_LAST) {
|
||||
sort.missing("_last");
|
||||
SortOrder sortOrder = order.getDirection().isDescending() ? SortOrder.DESC : SortOrder.ASC;
|
||||
|
||||
if (ScoreSortBuilder.NAME.equals(order.getProperty())) {
|
||||
ScoreSortBuilder sort = SortBuilders //
|
||||
.scoreSort() //
|
||||
.order(sortOrder);
|
||||
|
||||
sourceBuilder.sort(sort);
|
||||
} else {
|
||||
ElasticsearchPersistentProperty property = (entity != null) //
|
||||
? entity.getPersistentProperty(order.getProperty()) //
|
||||
: null;
|
||||
String fieldName = property != null ? property.getFieldName() : order.getProperty();
|
||||
|
||||
FieldSortBuilder sort = SortBuilders //
|
||||
.fieldSort(fieldName) //
|
||||
.order(sortOrder);
|
||||
|
||||
if (order.getNullHandling() == Sort.NullHandling.NULLS_FIRST) {
|
||||
sort.missing("_first");
|
||||
} else if (order.getNullHandling() == Sort.NullHandling.NULLS_LAST) {
|
||||
sort.missing("_last");
|
||||
}
|
||||
|
||||
sourceBuilder.sort(sort);
|
||||
}
|
||||
sourceBuilder.sort(sort);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1364,7 +1453,7 @@ public class ElasticsearchRestTemplate
|
||||
Assert.notNull(indexName, "No index defined for refresh()");
|
||||
try {
|
||||
// TODO: Do something with the response.
|
||||
client.indices().refresh(refreshRequest(indexName));
|
||||
client.indices().refresh(refreshRequest(indexName), RequestOptions.DEFAULT);
|
||||
} catch (IOException e) {
|
||||
throw new ElasticsearchException("failed to refresh index: " + indexName, e);
|
||||
}
|
||||
@@ -1375,48 +1464,6 @@ public class ElasticsearchRestTemplate
|
||||
refresh(getPersistentEntityFor(clazz).getIndexName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean addAlias(AliasQuery query) {
|
||||
Assert.notNull(query.getIndexName(), "No index defined for Alias");
|
||||
Assert.notNull(query.getAliasName(), "No alias defined");
|
||||
final IndicesAliasesRequest.AliasActions aliasAction = IndicesAliasesRequest.AliasActions.add()
|
||||
.alias(query.getAliasName()).index(query.getIndexName());
|
||||
|
||||
if (query.getFilterBuilder() != null) {
|
||||
aliasAction.filter(query.getFilterBuilder());
|
||||
} else if (query.getFilter() != null) {
|
||||
aliasAction.filter(query.getFilter());
|
||||
} else if (hasText(query.getRouting())) {
|
||||
aliasAction.routing(query.getRouting());
|
||||
} else if (hasText(query.getSearchRouting())) {
|
||||
aliasAction.searchRouting(query.getSearchRouting());
|
||||
} else if (hasText(query.getIndexRouting())) {
|
||||
aliasAction.indexRouting(query.getIndexRouting());
|
||||
}
|
||||
|
||||
IndicesAliasesRequest request = new IndicesAliasesRequest();
|
||||
request.addAliasAction(aliasAction);
|
||||
try {
|
||||
return client.indices().updateAliases(request).isAcknowledged();
|
||||
} catch (IOException e) {
|
||||
throw new ElasticsearchException("failed to update aliases with request: " + request, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean removeAlias(AliasQuery query) {
|
||||
Assert.notNull(query.getIndexName(), "No index defined for Alias");
|
||||
Assert.notNull(query.getAliasName(), "No alias defined");
|
||||
IndicesAliasesRequest request = new IndicesAliasesRequest();
|
||||
AliasActions aliasAction = new AliasActions(AliasActions.Type.REMOVE);
|
||||
request.addAliasAction(aliasAction);
|
||||
try {
|
||||
return client.indices().updateAliases(request).isAcknowledged();
|
||||
} catch (IOException e) {
|
||||
throw new ElasticsearchException("failed to update aliases with request: " + request, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AliasMetaData> queryForAlias(String indexName) {
|
||||
List<AliasMetaData> aliases = null;
|
||||
@@ -1441,16 +1488,17 @@ public class ElasticsearchRestTemplate
|
||||
* @param aliasResponse
|
||||
* @return
|
||||
*/
|
||||
List<AliasMetaData> convertAliasResponse(String aliasResponse) {
|
||||
private List<AliasMetaData> convertAliasResponse(String aliasResponse) {
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
|
||||
try {
|
||||
JsonNode node = mapper.readTree(aliasResponse);
|
||||
|
||||
Iterator<String> names = node.fieldNames();
|
||||
String name = names.next();
|
||||
node = node.findValue("aliases");
|
||||
|
||||
if (node == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
Map<String, AliasData> aliasData = mapper.readValue(mapper.writeValueAsString(node),
|
||||
new TypeReference<Map<String, AliasData>>() {});
|
||||
|
||||
@@ -1469,6 +1517,11 @@ public class ElasticsearchRestTemplate
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private ElasticsearchPersistentEntity<?> getPersistentEntity(@Nullable Class<?> clazz) {
|
||||
return clazz != null ? elasticsearchConverter.getMappingContext().getPersistentEntity(clazz) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ElasticsearchPersistentEntity getPersistentEntityFor(Class clazz) {
|
||||
Assert.isTrue(clazz.isAnnotationPresent(Document.class), "Unable to identify index name. " + clazz.getSimpleName()
|
||||
@@ -1560,34 +1613,9 @@ public class ElasticsearchRestTemplate
|
||||
return resultsMapper;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public static String readFileFromClasspath(String url) {
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
|
||||
BufferedReader bufferedReader = null;
|
||||
|
||||
try {
|
||||
ClassPathResource classPathResource = new ClassPathResource(url);
|
||||
InputStreamReader inputStreamReader = new InputStreamReader(classPathResource.getInputStream());
|
||||
bufferedReader = new BufferedReader(inputStreamReader);
|
||||
String line;
|
||||
|
||||
String lineSeparator = System.getProperty("line.separator");
|
||||
while ((line = bufferedReader.readLine()) != null) {
|
||||
stringBuilder.append(line).append(lineSeparator);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.debug(String.format("Failed to load file from url: %s: %s", url, e.getMessage()));
|
||||
return null;
|
||||
} finally {
|
||||
if (bufferedReader != null)
|
||||
try {
|
||||
bufferedReader.close();
|
||||
} catch (IOException e) {
|
||||
logger.debug(String.format("Unable to close buffered reader.. %s", e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
return stringBuilder.toString();
|
||||
return ResourceUtil.readFileFromClasspath(url);
|
||||
}
|
||||
|
||||
public SearchResponse suggest(SuggestBuilder suggestion, String... indices) {
|
||||
@@ -1597,7 +1625,7 @@ public class ElasticsearchRestTemplate
|
||||
searchRequest.source(sourceBuilder);
|
||||
|
||||
try {
|
||||
return client.search(searchRequest);
|
||||
return client.search(searchRequest, RequestOptions.DEFAULT);
|
||||
} catch (IOException e) {
|
||||
throw new ElasticsearchException("Could not execute search request : " + searchRequest.toString(), e);
|
||||
}
|
||||
|
||||
+183
-185
@@ -19,16 +19,12 @@ import static org.elasticsearch.client.Requests.*;
|
||||
import static org.elasticsearch.index.query.QueryBuilders.*;
|
||||
import static org.springframework.util.CollectionUtils.*;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.elasticsearch.action.ActionFuture;
|
||||
@@ -51,12 +47,12 @@ import org.elasticsearch.action.search.MultiSearchRequest;
|
||||
import org.elasticsearch.action.search.MultiSearchResponse;
|
||||
import org.elasticsearch.action.search.SearchRequestBuilder;
|
||||
import org.elasticsearch.action.search.SearchResponse;
|
||||
import org.elasticsearch.action.update.UpdateRequest;
|
||||
import org.elasticsearch.action.update.UpdateRequestBuilder;
|
||||
import org.elasticsearch.action.update.UpdateResponse;
|
||||
import org.elasticsearch.client.Client;
|
||||
import org.elasticsearch.client.Requests;
|
||||
import org.elasticsearch.cluster.metadata.AliasMetaData;
|
||||
import org.elasticsearch.common.Nullable;
|
||||
import org.elasticsearch.common.collect.MapBuilder;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
@@ -82,13 +78,11 @@ import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.elasticsearch.ElasticsearchException;
|
||||
import org.springframework.data.elasticsearch.annotations.Document;
|
||||
import org.springframework.data.elasticsearch.annotations.Mapping;
|
||||
import org.springframework.data.elasticsearch.annotations.Setting;
|
||||
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
|
||||
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
|
||||
@@ -99,6 +93,7 @@ import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersiste
|
||||
import org.springframework.data.elasticsearch.core.mapping.SimpleElasticsearchMappingContext;
|
||||
import org.springframework.data.elasticsearch.core.query.*;
|
||||
import org.springframework.data.util.CloseableIterator;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@@ -124,16 +119,19 @@ import org.springframework.util.StringUtils;
|
||||
* @author Ivan Greene
|
||||
* @author Christoph Strobl
|
||||
* @author Dmitriy Yakovlev
|
||||
* @author Peter-Josef Meisch
|
||||
* @author Martin Choraine
|
||||
* @author Farid Azaza
|
||||
* @author Gyula Attila Csorogi
|
||||
*/
|
||||
public class ElasticsearchTemplate implements ElasticsearchOperations, EsClient<Client>, ApplicationContextAware {
|
||||
public class ElasticsearchTemplate extends AbstractElasticsearchTemplate
|
||||
implements ElasticsearchOperations, EsClient<Client>, ApplicationContextAware {
|
||||
|
||||
private static final Logger QUERY_LOGGER = LoggerFactory
|
||||
.getLogger("org.springframework.data.elasticsearch.core.QUERY");
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(ElasticsearchTemplate.class);
|
||||
private static final String FIELD_SCORE = "_score";
|
||||
|
||||
private Client client;
|
||||
private ElasticsearchConverter elasticsearchConverter;
|
||||
private ResultsMapper resultsMapper;
|
||||
private String searchTimeout;
|
||||
|
||||
@@ -162,12 +160,12 @@ public class ElasticsearchTemplate implements ElasticsearchOperations, EsClient<
|
||||
public ElasticsearchTemplate(Client client, ElasticsearchConverter elasticsearchConverter,
|
||||
ResultsMapper resultsMapper) {
|
||||
|
||||
super(elasticsearchConverter);
|
||||
|
||||
Assert.notNull(client, "Client must not be null!");
|
||||
Assert.notNull(elasticsearchConverter, "ElasticsearchConverter must not be null!");
|
||||
Assert.notNull(resultsMapper, "ResultsMapper must not be null!");
|
||||
|
||||
this.client = client;
|
||||
this.elasticsearchConverter = elasticsearchConverter;
|
||||
this.resultsMapper = resultsMapper;
|
||||
}
|
||||
|
||||
@@ -180,6 +178,35 @@ public class ElasticsearchTemplate implements ElasticsearchOperations, EsClient<
|
||||
this.searchTimeout = searchTimeout;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAlias(AliasQuery query) {
|
||||
Assert.notNull(query.getIndexName(), "No index defined for Alias");
|
||||
Assert.notNull(query.getAliasName(), "No alias defined");
|
||||
IndicesAliasesRequest.AliasActions aliasAction = IndicesAliasesRequest.AliasActions.add()
|
||||
.alias(query.getAliasName()).index(query.getIndexName());
|
||||
|
||||
if (query.getFilterBuilder() != null) {
|
||||
aliasAction.filter(query.getFilterBuilder());
|
||||
} else if (query.getFilter() != null) {
|
||||
aliasAction.filter(query.getFilter());
|
||||
} else if (!StringUtils.isEmpty(query.getRouting())) {
|
||||
aliasAction.routing(query.getRouting());
|
||||
} else if (!StringUtils.isEmpty(query.getSearchRouting())) {
|
||||
aliasAction.searchRouting(query.getSearchRouting());
|
||||
} else if (!StringUtils.isEmpty(query.getIndexRouting())) {
|
||||
aliasAction.indexRouting(query.getIndexRouting());
|
||||
}
|
||||
return client.admin().indices().prepareAliases().addAliasAction(aliasAction).execute().actionGet().isAcknowledged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeAlias(AliasQuery query) {
|
||||
Assert.notNull(query.getIndexName(), "No index defined for Alias");
|
||||
Assert.notNull(query.getAliasName(), "No alias defined");
|
||||
return client.admin().indices().prepareAliases().removeAlias(query.getIndexName(), query.getAliasName()).execute()
|
||||
.actionGet().isAcknowledged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> boolean createIndex(Class<T> clazz) {
|
||||
return createIndexIfNotCreated(clazz);
|
||||
@@ -193,23 +220,7 @@ public class ElasticsearchTemplate implements ElasticsearchOperations, EsClient<
|
||||
|
||||
@Override
|
||||
public <T> boolean putMapping(Class<T> clazz) {
|
||||
if (clazz.isAnnotationPresent(Mapping.class)) {
|
||||
String mappingPath = clazz.getAnnotation(Mapping.class).mappingPath();
|
||||
if (!StringUtils.isEmpty(mappingPath)) {
|
||||
String mappings = readFileFromClasspath(mappingPath);
|
||||
if (!StringUtils.isEmpty(mappings)) {
|
||||
return putMapping(clazz, mappings);
|
||||
}
|
||||
} else {
|
||||
LOGGER.info("mappingPath in @Mapping has to be defined. Building mappings using @Field");
|
||||
}
|
||||
}
|
||||
try {
|
||||
MappingBuilder mappingBuilder = new MappingBuilder(elasticsearchConverter);
|
||||
return putMapping(clazz, mappingBuilder.buildPropertyMapping(clazz));
|
||||
} catch (Exception e) {
|
||||
throw new ElasticsearchException("Failed to build mapping for " + clazz.getSimpleName(), e);
|
||||
}
|
||||
return putMapping(clazz, buildMapping(clazz));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -218,6 +229,11 @@ public class ElasticsearchTemplate implements ElasticsearchOperations, EsClient<
|
||||
mapping);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> boolean putMapping(String indexName, String type, Class<T> clazz) {
|
||||
return putMapping(indexName, type, buildMapping(clazz));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean putMapping(String indexName, String type, Object mapping) {
|
||||
Assert.notNull(indexName, "No index defined for putMapping()");
|
||||
@@ -234,10 +250,10 @@ public class ElasticsearchTemplate implements ElasticsearchOperations, EsClient<
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map getMapping(String indexName, String type) {
|
||||
public Map<String, Object> getMapping(String indexName, String type) {
|
||||
Assert.notNull(indexName, "No index defined for putMapping()");
|
||||
Assert.notNull(type, "No type defined for putMapping()");
|
||||
Map mappings = null;
|
||||
Map<String, Object> mappings = null;
|
||||
try {
|
||||
mappings = client.admin().indices().getMappings(new GetMappingsRequest().indices(indexName).types(type))
|
||||
.actionGet().getMappings().get(indexName).get(type).getSourceAsMap();
|
||||
@@ -249,7 +265,7 @@ public class ElasticsearchTemplate implements ElasticsearchOperations, EsClient<
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Map getMapping(Class<T> clazz) {
|
||||
public <T> Map<String, Object> getMapping(Class<T> clazz) {
|
||||
return getMapping(getPersistentEntityFor(clazz).getIndexName(), getPersistentEntityFor(clazz).getIndexType());
|
||||
}
|
||||
|
||||
@@ -362,7 +378,7 @@ public class ElasticsearchTemplate implements ElasticsearchOperations, EsClient<
|
||||
|
||||
@Override
|
||||
public <T> T query(SearchQuery query, ResultsExtractor<T> resultsExtractor) {
|
||||
SearchResponse response = doSearch(prepareSearch(query), query);
|
||||
SearchResponse response = doSearch(prepareSearch(query, (ElasticsearchPersistentEntity) null), query);
|
||||
return resultsExtractor.extract(response);
|
||||
}
|
||||
|
||||
@@ -383,7 +399,8 @@ public class ElasticsearchTemplate implements ElasticsearchOperations, EsClient<
|
||||
|
||||
@Override
|
||||
public <T> List<String> queryForIds(SearchQuery query) {
|
||||
SearchRequestBuilder request = prepareSearch(query).setQuery(query.getQuery());
|
||||
SearchRequestBuilder request = prepareSearch(query, (ElasticsearchPersistentEntity) null)
|
||||
.setQuery(query.getQuery());
|
||||
if (query.getFilter() != null) {
|
||||
request.setPostFilter(query.getFilter());
|
||||
}
|
||||
@@ -428,9 +445,8 @@ public class ElasticsearchTemplate implements ElasticsearchOperations, EsClient<
|
||||
|
||||
@Override
|
||||
public <T> CloseableIterator<T> stream(CriteriaQuery query, Class<T> clazz) {
|
||||
final long scrollTimeInMillis = TimeValue.timeValueMinutes(1).millis();
|
||||
return doStream(scrollTimeInMillis, startScroll(scrollTimeInMillis, query, clazz), clazz,
|
||||
resultsMapper);
|
||||
long scrollTimeInMillis = TimeValue.timeValueMinutes(1).millis();
|
||||
return doStream(scrollTimeInMillis, startScroll(scrollTimeInMillis, query, clazz), clazz, resultsMapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -439,69 +455,15 @@ public class ElasticsearchTemplate implements ElasticsearchOperations, EsClient<
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> CloseableIterator<T> stream(SearchQuery query, final Class<T> clazz, final SearchResultMapper mapper) {
|
||||
final long scrollTimeInMillis = TimeValue.timeValueMinutes(1).millis();
|
||||
return doStream(scrollTimeInMillis, startScroll(scrollTimeInMillis, query, clazz, mapper), clazz,
|
||||
mapper);
|
||||
public <T> CloseableIterator<T> stream(SearchQuery query, Class<T> clazz, SearchResultMapper mapper) {
|
||||
long scrollTimeInMillis = TimeValue.timeValueMinutes(1).millis();
|
||||
return doStream(scrollTimeInMillis, startScroll(scrollTimeInMillis, query, clazz, mapper), clazz, mapper);
|
||||
}
|
||||
|
||||
private <T> CloseableIterator<T> doStream(final long scrollTimeInMillis, final ScrolledPage<T> page,
|
||||
final Class<T> clazz, final SearchResultMapper mapper) {
|
||||
return new CloseableIterator<T>() {
|
||||
|
||||
/** As we couldn't retrieve single result with scroll, store current hits. */
|
||||
private volatile Iterator<T> currentHits = page.iterator();
|
||||
|
||||
/** The scroll id. */
|
||||
private volatile String scrollId = page.getScrollId();
|
||||
|
||||
/** If stream is finished (ie: cluster returns no results. */
|
||||
private volatile boolean finished = !currentHits.hasNext();
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
try {
|
||||
// Clear scroll on cluster only in case of error (cause elasticsearch auto clear scroll when it's done)
|
||||
if (!finished && scrollId != null && currentHits != null && currentHits.hasNext()) {
|
||||
clearScroll(scrollId);
|
||||
}
|
||||
} finally {
|
||||
currentHits = null;
|
||||
scrollId = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
// Test if stream is finished
|
||||
if (finished) {
|
||||
return false;
|
||||
}
|
||||
// Test if it remains hits
|
||||
if (currentHits == null || !currentHits.hasNext()) {
|
||||
// Do a new request
|
||||
final ScrolledPage<T> scroll = continueScroll(scrollId, scrollTimeInMillis, clazz, mapper);
|
||||
// Save hits and scroll id
|
||||
currentHits = scroll.iterator();
|
||||
finished = !currentHits.hasNext();
|
||||
scrollId = scroll.getScrollId();
|
||||
}
|
||||
return currentHits.hasNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public T next() {
|
||||
if (hasNext()) {
|
||||
return currentHits.next();
|
||||
}
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException("remove");
|
||||
}
|
||||
};
|
||||
private <T> CloseableIterator<T> doStream(long scrollTimeInMillis, ScrolledPage<T> page, Class<T> clazz,
|
||||
SearchResultMapper mapper) {
|
||||
return StreamQueries.streamResults(page, scrollId -> continueScroll(scrollId, scrollTimeInMillis, clazz, mapper),
|
||||
this::clearScroll);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -546,6 +508,7 @@ public class ElasticsearchTemplate implements ElasticsearchOperations, EsClient<
|
||||
if (elasticsearchQuery != null) {
|
||||
countRequestBuilder.setQuery(elasticsearchQuery);
|
||||
}
|
||||
countRequestBuilder.setSize(0);
|
||||
return countRequestBuilder.execute().actionGet().getHits().getTotalHits();
|
||||
}
|
||||
|
||||
@@ -559,6 +522,7 @@ public class ElasticsearchTemplate implements ElasticsearchOperations, EsClient<
|
||||
if (elasticsearchFilter != null) {
|
||||
searchRequestBuilder.setPostFilter(elasticsearchFilter);
|
||||
}
|
||||
searchRequestBuilder.setSize(0);
|
||||
return searchRequestBuilder.execute().actionGet().getHits().getTotalHits();
|
||||
}
|
||||
|
||||
@@ -581,7 +545,7 @@ public class ElasticsearchTemplate implements ElasticsearchOperations, EsClient<
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> LinkedList<T> multiGet(SearchQuery searchQuery, Class<T> clazz) {
|
||||
public <T> List<T> multiGet(SearchQuery searchQuery, Class<T> clazz) {
|
||||
return resultsMapper.mapResults(getMultiResponse(searchQuery, clazz), clazz);
|
||||
}
|
||||
|
||||
@@ -616,7 +580,7 @@ public class ElasticsearchTemplate implements ElasticsearchOperations, EsClient<
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> LinkedList<T> multiGet(SearchQuery searchQuery, Class<T> clazz, MultiGetResultMapper getResultMapper) {
|
||||
public <T> List<T> multiGet(SearchQuery searchQuery, Class<T> clazz, MultiGetResultMapper getResultMapper) {
|
||||
return getResultMapper.mapResults(getMultiResponse(searchQuery, clazz), clazz);
|
||||
}
|
||||
|
||||
@@ -636,35 +600,53 @@ public class ElasticsearchTemplate implements ElasticsearchOperations, EsClient<
|
||||
}
|
||||
|
||||
private UpdateRequestBuilder prepareUpdate(UpdateQuery query) {
|
||||
|
||||
String indexName = !StringUtils.isEmpty(query.getIndexName()) ? query.getIndexName()
|
||||
: getPersistentEntityFor(query.getClazz()).getIndexName();
|
||||
String type = !StringUtils.isEmpty(query.getType()) ? query.getType()
|
||||
: getPersistentEntityFor(query.getClazz()).getIndexType();
|
||||
|
||||
Assert.notNull(indexName, "No index defined for Query");
|
||||
Assert.notNull(type, "No type define for Query");
|
||||
Assert.notNull(query.getId(), "No Id define for Query");
|
||||
Assert.notNull(query.getUpdateRequest(), "No IndexRequest define for Query");
|
||||
UpdateRequestBuilder updateRequestBuilder = client.prepareUpdate(indexName, type, query.getId());
|
||||
updateRequestBuilder.setRouting(query.getUpdateRequest().routing());
|
||||
Assert.notNull(query.getUpdateRequest(), "No UpdateRequest define for Query");
|
||||
|
||||
if (query.getUpdateRequest().script() == null) {
|
||||
// doc
|
||||
if (query.DoUpsert()) {
|
||||
updateRequestBuilder.setDocAsUpsert(true).setDoc(query.getUpdateRequest().doc());
|
||||
} else {
|
||||
updateRequestBuilder.setDoc(query.getUpdateRequest().doc());
|
||||
}
|
||||
} else {
|
||||
// or script
|
||||
updateRequestBuilder.setScript(query.getUpdateRequest().script());
|
||||
UpdateRequest queryUpdateRequest = query.getUpdateRequest();
|
||||
|
||||
UpdateRequestBuilder updateRequestBuilder = client.prepareUpdate(indexName, type, query.getId()) //
|
||||
.setRouting(queryUpdateRequest.routing()) //
|
||||
.setRetryOnConflict(queryUpdateRequest.retryOnConflict()) //
|
||||
.setTimeout(queryUpdateRequest.timeout()) //
|
||||
.setWaitForActiveShards(queryUpdateRequest.waitForActiveShards()) //
|
||||
.setRefreshPolicy(queryUpdateRequest.getRefreshPolicy()) //
|
||||
.setWaitForActiveShards(queryUpdateRequest.waitForActiveShards()) //
|
||||
.setScriptedUpsert(queryUpdateRequest.scriptedUpsert()) //
|
||||
.setDocAsUpsert(queryUpdateRequest.docAsUpsert());
|
||||
|
||||
if (query.DoUpsert()) {
|
||||
updateRequestBuilder.setDocAsUpsert(true);
|
||||
}
|
||||
if (queryUpdateRequest.script() != null) {
|
||||
updateRequestBuilder.setScript(queryUpdateRequest.script());
|
||||
}
|
||||
if (queryUpdateRequest.doc() != null) {
|
||||
updateRequestBuilder.setDoc(queryUpdateRequest.doc());
|
||||
}
|
||||
if (queryUpdateRequest.upsertRequest() != null) {
|
||||
updateRequestBuilder.setUpsert(queryUpdateRequest.upsertRequest());
|
||||
}
|
||||
|
||||
return updateRequestBuilder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bulkIndex(List<IndexQuery> queries) {
|
||||
public void bulkIndex(List<IndexQuery> queries, BulkOptions bulkOptions) {
|
||||
|
||||
Assert.notNull(queries, "List of IndexQuery must not be null");
|
||||
Assert.notNull(bulkOptions, "BulkOptions must not be null");
|
||||
|
||||
BulkRequestBuilder bulkRequest = client.prepareBulk();
|
||||
setBulkOptions(bulkRequest, bulkOptions);
|
||||
for (IndexQuery query : queries) {
|
||||
bulkRequest.add(prepareIndex(query));
|
||||
}
|
||||
@@ -672,14 +654,42 @@ public class ElasticsearchTemplate implements ElasticsearchOperations, EsClient<
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bulkUpdate(List<UpdateQuery> queries) {
|
||||
public void bulkUpdate(List<UpdateQuery> queries, BulkOptions bulkOptions) {
|
||||
|
||||
Assert.notNull(queries, "List of UpdateQuery must not be null");
|
||||
Assert.notNull(bulkOptions, "BulkOptions must not be null");
|
||||
|
||||
BulkRequestBuilder bulkRequest = client.prepareBulk();
|
||||
setBulkOptions(bulkRequest, bulkOptions);
|
||||
for (UpdateQuery query : queries) {
|
||||
bulkRequest.add(prepareUpdate(query));
|
||||
}
|
||||
checkForBulkUpdateFailure(bulkRequest.execute().actionGet());
|
||||
}
|
||||
|
||||
private static void setBulkOptions(BulkRequestBuilder bulkRequest, BulkOptions bulkOptions) {
|
||||
|
||||
if (bulkOptions.getTimeout() != null) {
|
||||
bulkRequest.setTimeout(bulkOptions.getTimeout());
|
||||
}
|
||||
|
||||
if (bulkOptions.getRefreshPolicy() != null) {
|
||||
bulkRequest.setRefreshPolicy(bulkOptions.getRefreshPolicy());
|
||||
}
|
||||
|
||||
if (bulkOptions.getWaitForActiveShards() != null) {
|
||||
bulkRequest.setWaitForActiveShards(bulkOptions.getWaitForActiveShards());
|
||||
}
|
||||
|
||||
if (bulkOptions.getPipeline() != null) {
|
||||
bulkRequest.pipeline(bulkOptions.getPipeline());
|
||||
}
|
||||
|
||||
if (bulkOptions.getRoutingId() != null) {
|
||||
bulkRequest.routing(bulkOptions.getRoutingId());
|
||||
}
|
||||
}
|
||||
|
||||
private void checkForBulkUpdateFailure(BulkResponse bulkResponse) {
|
||||
if (bulkResponse.hasFailures()) {
|
||||
Map<String, String> failedDocuments = new HashMap<>();
|
||||
@@ -776,10 +786,11 @@ public class ElasticsearchTemplate implements ElasticsearchOperations, EsClient<
|
||||
|
||||
private <T> SearchRequestBuilder prepareScroll(Query query, long scrollTimeInMillis, Class<T> clazz) {
|
||||
setPersistentEntityIndexAndType(query, clazz);
|
||||
return prepareScroll(query, scrollTimeInMillis);
|
||||
return prepareScroll(query, scrollTimeInMillis, getPersistentEntity(clazz));
|
||||
}
|
||||
|
||||
private SearchRequestBuilder prepareScroll(Query query, long scrollTimeInMillis) {
|
||||
private SearchRequestBuilder prepareScroll(Query query, long scrollTimeInMillis,
|
||||
@Nullable ElasticsearchPersistentEntity<?> entity) {
|
||||
SearchRequestBuilder requestBuilder = client.prepareSearch(toArray(query.getIndices()))
|
||||
.setTypes(toArray(query.getTypes())).setScroll(TimeValue.timeValueMillis(scrollTimeInMillis)).setFrom(0)
|
||||
.setVersion(true);
|
||||
@@ -798,7 +809,28 @@ public class ElasticsearchTemplate implements ElasticsearchOperations, EsClient<
|
||||
}
|
||||
|
||||
if (query.getSort() != null) {
|
||||
prepareSort(query, requestBuilder);
|
||||
prepareSort(query, requestBuilder, entity);
|
||||
}
|
||||
|
||||
if (query.getIndicesOptions() != null) {
|
||||
requestBuilder.setIndicesOptions(query.getIndicesOptions());
|
||||
}
|
||||
|
||||
if (query instanceof SearchQuery) {
|
||||
SearchQuery searchQuery = (SearchQuery) query;
|
||||
|
||||
if (searchQuery.getHighlightFields() != null || searchQuery.getHighlightBuilder() != null) {
|
||||
HighlightBuilder highlightBuilder = searchQuery.getHighlightBuilder();
|
||||
if (highlightBuilder == null) {
|
||||
highlightBuilder = new HighlightBuilder();
|
||||
}
|
||||
if (searchQuery.getHighlightFields() != null) {
|
||||
for (HighlightBuilder.Field highlightField : searchQuery.getHighlightFields()) {
|
||||
highlightBuilder.field(highlightField);
|
||||
}
|
||||
}
|
||||
requestBuilder.highlighter(highlightBuilder);
|
||||
}
|
||||
}
|
||||
|
||||
return requestBuilder;
|
||||
@@ -951,6 +983,10 @@ public class ElasticsearchTemplate implements ElasticsearchOperations, EsClient<
|
||||
}
|
||||
}
|
||||
|
||||
if (searchQuery.getCollapseBuilder() != null) {
|
||||
searchRequest.setCollapse(searchQuery.getCollapseBuilder());
|
||||
}
|
||||
|
||||
if (searchQuery.getHighlightFields() != null || searchQuery.getHighlightBuilder() != null) {
|
||||
HighlightBuilder highlightBuilder = searchQuery.getHighlightBuilder();
|
||||
if (highlightBuilder == null) {
|
||||
@@ -981,6 +1017,7 @@ public class ElasticsearchTemplate implements ElasticsearchOperations, EsClient<
|
||||
searchRequest.addAggregation(aggregatedFacet.getFacet());
|
||||
}
|
||||
}
|
||||
|
||||
return searchRequest.setQuery(searchQuery.getQuery());
|
||||
}
|
||||
|
||||
@@ -1005,7 +1042,7 @@ public class ElasticsearchTemplate implements ElasticsearchOperations, EsClient<
|
||||
if (clazz.isAnnotationPresent(Setting.class)) {
|
||||
String settingPath = clazz.getAnnotation(Setting.class).settingPath();
|
||||
if (!StringUtils.isEmpty(settingPath)) {
|
||||
String settings = readFileFromClasspath(settingPath);
|
||||
String settings = ResourceUtil.readFileFromClasspath(settingPath);
|
||||
if (!StringUtils.isEmpty(settings)) {
|
||||
return createIndex(getPersistentEntityFor(clazz).getIndexName(), settings);
|
||||
}
|
||||
@@ -1034,10 +1071,10 @@ public class ElasticsearchTemplate implements ElasticsearchOperations, EsClient<
|
||||
return createIndex(getPersistentEntityFor(clazz).getIndexName(), settings);
|
||||
}
|
||||
|
||||
private <T> Map getDefaultSettings(ElasticsearchPersistentEntity<T> persistentEntity) {
|
||||
private <T> Map<String, String> getDefaultSettings(ElasticsearchPersistentEntity<T> persistentEntity) {
|
||||
|
||||
if (persistentEntity.isUseServerConfiguration())
|
||||
return new HashMap();
|
||||
return new HashMap<>();
|
||||
|
||||
return new MapBuilder<String, String>().put("index.number_of_shards", String.valueOf(persistentEntity.getShards()))
|
||||
.put("index.number_of_replicas", String.valueOf(persistentEntity.getReplicas()))
|
||||
@@ -1046,12 +1083,12 @@ public class ElasticsearchTemplate implements ElasticsearchOperations, EsClient<
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Map getSetting(Class<T> clazz) {
|
||||
public <T> Map<String, Object> getSetting(Class<T> clazz) {
|
||||
return getSetting(getPersistentEntityFor(clazz).getIndexName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map getSetting(String indexName) {
|
||||
public Map<String, Object> getSetting(String indexName) {
|
||||
Assert.notNull(indexName, "No index defined for getSettings");
|
||||
Settings settings = client.admin().indices().getSettings(new GetSettingsRequest()).actionGet().getIndexToSettings()
|
||||
.get(indexName);
|
||||
@@ -1060,10 +1097,10 @@ public class ElasticsearchTemplate implements ElasticsearchOperations, EsClient<
|
||||
|
||||
private <T> SearchRequestBuilder prepareSearch(Query query, Class<T> clazz) {
|
||||
setPersistentEntityIndexAndType(query, clazz);
|
||||
return prepareSearch(query);
|
||||
return prepareSearch(query, getPersistentEntity(clazz));
|
||||
}
|
||||
|
||||
private SearchRequestBuilder prepareSearch(Query query) {
|
||||
private SearchRequestBuilder prepareSearch(Query query, @Nullable ElasticsearchPersistentEntity<?> entity) {
|
||||
Assert.notNull(query.getIndices(), "No index defined for Query");
|
||||
Assert.notNull(query.getTypes(), "No type defined for Query");
|
||||
|
||||
@@ -1092,28 +1129,38 @@ public class ElasticsearchTemplate implements ElasticsearchOperations, EsClient<
|
||||
}
|
||||
|
||||
if (query.getSort() != null) {
|
||||
prepareSort(query, searchRequestBuilder);
|
||||
prepareSort(query, searchRequestBuilder, entity);
|
||||
}
|
||||
|
||||
if (query.getMinScore() > 0) {
|
||||
searchRequestBuilder.setMinScore(query.getMinScore());
|
||||
}
|
||||
|
||||
if (query.getPreference() != null) {
|
||||
searchRequestBuilder.setPreference(query.getPreference());
|
||||
}
|
||||
|
||||
return searchRequestBuilder;
|
||||
}
|
||||
|
||||
private void prepareSort(Query query, SearchRequestBuilder searchRequestBuilder) {
|
||||
private void prepareSort(Query query, SearchRequestBuilder searchRequestBuilder,
|
||||
@Nullable ElasticsearchPersistentEntity<?> entity) {
|
||||
for (Sort.Order order : query.getSort()) {
|
||||
SortOrder sortOrder = order.getDirection().isDescending() ? SortOrder.DESC : SortOrder.ASC;
|
||||
|
||||
if (FIELD_SCORE.equals(order.getProperty())) {
|
||||
if (ScoreSortBuilder.NAME.equals(order.getProperty())) {
|
||||
ScoreSortBuilder sort = SortBuilders //
|
||||
.scoreSort() //
|
||||
.order(sortOrder);
|
||||
|
||||
searchRequestBuilder.addSort(sort);
|
||||
} else {
|
||||
ElasticsearchPersistentProperty property = entity != null //
|
||||
? entity.getPersistentProperty(order.getProperty()) //
|
||||
: null;
|
||||
String fieldName = property != null ? property.getFieldName() : order.getProperty();
|
||||
FieldSortBuilder sort = SortBuilders //
|
||||
.fieldSort(order.getProperty()) //
|
||||
.fieldSort(fieldName) //
|
||||
.order(sortOrder);
|
||||
|
||||
if (order.getNullHandling() == Sort.NullHandling.NULLS_FIRST) {
|
||||
@@ -1182,41 +1229,17 @@ public class ElasticsearchTemplate implements ElasticsearchOperations, EsClient<
|
||||
refresh(getPersistentEntityFor(clazz).getIndexName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean addAlias(AliasQuery query) {
|
||||
Assert.notNull(query.getIndexName(), "No index defined for Alias");
|
||||
Assert.notNull(query.getAliasName(), "No alias defined");
|
||||
final IndicesAliasesRequest.AliasActions aliasAction = IndicesAliasesRequest.AliasActions.add()
|
||||
.alias(query.getAliasName()).index(query.getIndexName());
|
||||
|
||||
if (query.getFilterBuilder() != null) {
|
||||
aliasAction.filter(query.getFilterBuilder());
|
||||
} else if (query.getFilter() != null) {
|
||||
aliasAction.filter(query.getFilter());
|
||||
} else if (!StringUtils.isEmpty(query.getRouting())) {
|
||||
aliasAction.routing(query.getRouting());
|
||||
} else if (!StringUtils.isEmpty(query.getSearchRouting())) {
|
||||
aliasAction.searchRouting(query.getSearchRouting());
|
||||
} else if (!StringUtils.isEmpty(query.getIndexRouting())) {
|
||||
aliasAction.indexRouting(query.getIndexRouting());
|
||||
}
|
||||
return client.admin().indices().prepareAliases().addAliasAction(aliasAction).execute().actionGet().isAcknowledged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean removeAlias(AliasQuery query) {
|
||||
Assert.notNull(query.getIndexName(), "No index defined for Alias");
|
||||
Assert.notNull(query.getAliasName(), "No alias defined");
|
||||
return client.admin().indices().prepareAliases().removeAlias(query.getIndexName(), query.getAliasName()).execute()
|
||||
.actionGet().isAcknowledged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AliasMetaData> queryForAlias(String indexName) {
|
||||
return client.admin().indices().getAliases(new GetAliasesRequest().indices(indexName)).actionGet().getAliases()
|
||||
.get(indexName);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private ElasticsearchPersistentEntity<?> getPersistentEntity(@Nullable Class<?> clazz) {
|
||||
return clazz != null ? elasticsearchConverter.getMappingContext().getPersistentEntity(clazz) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ElasticsearchPersistentEntity getPersistentEntityFor(Class clazz) {
|
||||
Assert.isTrue(clazz.isAnnotationPresent(Document.class), "Unable to identify index name. " + clazz.getSimpleName()
|
||||
@@ -1308,34 +1331,9 @@ public class ElasticsearchTemplate implements ElasticsearchOperations, EsClient<
|
||||
return resultsMapper;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public static String readFileFromClasspath(String url) {
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
|
||||
BufferedReader bufferedReader = null;
|
||||
|
||||
try {
|
||||
ClassPathResource classPathResource = new ClassPathResource(url);
|
||||
InputStreamReader inputStreamReader = new InputStreamReader(classPathResource.getInputStream());
|
||||
bufferedReader = new BufferedReader(inputStreamReader);
|
||||
String line;
|
||||
|
||||
String lineSeparator = System.getProperty("line.separator");
|
||||
while ((line = bufferedReader.readLine()) != null) {
|
||||
stringBuilder.append(line).append(lineSeparator);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOGGER.debug(String.format("Failed to load file from url: %s: %s", url, e.getMessage()));
|
||||
return null;
|
||||
} finally {
|
||||
if (bufferedReader != null)
|
||||
try {
|
||||
bufferedReader.close();
|
||||
} catch (IOException e) {
|
||||
LOGGER.debug(String.format("Unable to close buffered reader.. %s", e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
return stringBuilder.toString();
|
||||
return ResourceUtil.readFileFromClasspath(url);
|
||||
}
|
||||
|
||||
public SearchResponse suggest(SuggestBuilder suggestion, String... indices) {
|
||||
|
||||
@@ -418,7 +418,7 @@ class MappingBuilder {
|
||||
String mappingPath = entity.getRequiredAnnotation(DynamicTemplates.class).mappingPath();
|
||||
if (hasText(mappingPath)) {
|
||||
|
||||
String jsonString = ElasticsearchTemplate.readFileFromClasspath(mappingPath);
|
||||
String jsonString = ResourceUtil.readFileFromClasspath(mappingPath);
|
||||
if (hasText(jsonString)) {
|
||||
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
package org.springframework.data.elasticsearch.core;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import org.elasticsearch.action.get.MultiGetResponse;
|
||||
|
||||
@@ -25,5 +25,5 @@ import org.elasticsearch.action.get.MultiGetResponse;
|
||||
*/
|
||||
public interface MultiGetResultMapper {
|
||||
|
||||
<T> LinkedList<T> mapResults(MultiGetResponse responses, Class<T> clazz);
|
||||
<T> List<T> mapResults(MultiGetResponse responses, Class<T> clazz);
|
||||
}
|
||||
|
||||
+157
-58
@@ -36,6 +36,7 @@ import org.elasticsearch.action.support.IndicesOptions;
|
||||
import org.elasticsearch.action.support.WriteRequest;
|
||||
import org.elasticsearch.action.support.WriteRequest.RefreshPolicy;
|
||||
import org.elasticsearch.client.Requests;
|
||||
import org.elasticsearch.client.core.CountRequest;
|
||||
import org.elasticsearch.index.get.GetResult;
|
||||
import org.elasticsearch.index.query.QueryBuilder;
|
||||
import org.elasticsearch.index.query.QueryBuilders;
|
||||
@@ -50,7 +51,6 @@ import org.elasticsearch.search.sort.SortOrder;
|
||||
import org.reactivestreams.Publisher;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.elasticsearch.NoSuchIndexException;
|
||||
@@ -76,6 +76,10 @@ import org.springframework.util.Assert;
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
* @author Farid Azaza
|
||||
* @author Martin Choraine
|
||||
* @author Peter-Josef Meisch
|
||||
* @author Mathias Teier
|
||||
* @since 3.2
|
||||
*/
|
||||
public class ReactiveElasticsearchTemplate implements ReactiveElasticsearchOperations {
|
||||
@@ -244,68 +248,133 @@ public class ReactiveElasticsearchTemplate implements ReactiveElasticsearchOpera
|
||||
@Nullable String type) {
|
||||
|
||||
return Flux.defer(() -> {
|
||||
SearchRequest request = prepareSearchRequest(buildSearchRequest(query, entity, index, type));
|
||||
|
||||
IndexCoordinates indexCoordinates = operations.determineIndex(entity, index, type);
|
||||
SearchRequest request = new SearchRequest(indices(query, indexCoordinates::getIndexName));
|
||||
request.types(indexTypes(query, indexCoordinates::getTypeName));
|
||||
|
||||
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
|
||||
searchSourceBuilder.query(mappedQuery(query, entity));
|
||||
searchSourceBuilder.version(entity.hasVersionProperty());
|
||||
searchSourceBuilder.trackScores(query.getTrackScores());
|
||||
|
||||
QueryBuilder postFilterQuery = mappedFilterQuery(query, entity);
|
||||
if (postFilterQuery != null) {
|
||||
searchSourceBuilder.postFilter(postFilterQuery);
|
||||
}
|
||||
|
||||
if (query.getSourceFilter() != null) {
|
||||
searchSourceBuilder.fetchSource(query.getSourceFilter().getIncludes(), query.getSourceFilter().getExcludes());
|
||||
}
|
||||
|
||||
sort(query, entity).forEach(searchSourceBuilder::sort);
|
||||
|
||||
if (query.getMinScore() > 0) {
|
||||
searchSourceBuilder.minScore(query.getMinScore());
|
||||
}
|
||||
|
||||
if (query.getIndicesOptions() != null) {
|
||||
request.indicesOptions(query.getIndicesOptions());
|
||||
}
|
||||
|
||||
Pageable pageable = query.getPageable();
|
||||
|
||||
if (pageable.isPaged()) {
|
||||
|
||||
long offset = pageable.getOffset();
|
||||
if (offset > Integer.MAX_VALUE) {
|
||||
throw new IllegalArgumentException(String.format("Offset must not be more than %s", Integer.MAX_VALUE));
|
||||
}
|
||||
|
||||
searchSourceBuilder.from((int) offset);
|
||||
searchSourceBuilder.size(pageable.getPageSize());
|
||||
|
||||
request.source(searchSourceBuilder);
|
||||
return doFind(prepareSearchRequest(request));
|
||||
|
||||
if (query.getPageable().isPaged()) {
|
||||
return doFind(request);
|
||||
} else {
|
||||
|
||||
request.source(searchSourceBuilder);
|
||||
return doScan(prepareSearchRequest(request));
|
||||
return doScroll(request);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Long> count(Query query, Class<?> entityType, String index, String type) {
|
||||
return doCount(query, getPersistentEntity(entityType), index, type);
|
||||
}
|
||||
|
||||
private Mono<Long> doCount(Query query, ElasticsearchPersistentEntity<?> entity, @Nullable String index,
|
||||
@Nullable String type) {
|
||||
return Mono.defer(() -> {
|
||||
|
||||
CountRequest countRequest = buildCountRequest(query, entity, index, type);
|
||||
CountRequest request = prepareCountRequest(countRequest);
|
||||
return doCount(request);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private CountRequest buildCountRequest(Query query, ElasticsearchPersistentEntity<?> entity, @Nullable String index,
|
||||
@Nullable String type) {
|
||||
|
||||
IndexCoordinates indexCoordinates = operations.determineIndex(entity, index, type);
|
||||
CountRequest request = new CountRequest(indices(query, indexCoordinates::getIndexName));
|
||||
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
|
||||
searchSourceBuilder.query(mappedQuery(query, entity));
|
||||
searchSourceBuilder.trackScores(query.getTrackScores());
|
||||
|
||||
QueryBuilder postFilterQuery = mappedFilterQuery(query, entity);
|
||||
if (postFilterQuery != null) {
|
||||
searchSourceBuilder.postFilter(postFilterQuery);
|
||||
}
|
||||
|
||||
if (query.getSourceFilter() != null) {
|
||||
searchSourceBuilder.fetchSource(query.getSourceFilter().getIncludes(), query.getSourceFilter().getExcludes());
|
||||
}
|
||||
|
||||
if (query instanceof SearchQuery && ((SearchQuery) query).getCollapseBuilder() != null) {
|
||||
searchSourceBuilder.collapse(((SearchQuery) query).getCollapseBuilder());
|
||||
}
|
||||
|
||||
sort(query, entity).forEach(searchSourceBuilder::sort);
|
||||
|
||||
if (query.getMinScore() > 0) {
|
||||
searchSourceBuilder.minScore(query.getMinScore());
|
||||
}
|
||||
|
||||
if (query.getIndicesOptions() != null) {
|
||||
request.indicesOptions(query.getIndicesOptions());
|
||||
}
|
||||
|
||||
if (query.getPreference() != null) {
|
||||
request.preference(query.getPreference());
|
||||
}
|
||||
request.source(searchSourceBuilder);
|
||||
return request;
|
||||
}
|
||||
|
||||
private SearchRequest buildSearchRequest(Query query, ElasticsearchPersistentEntity<?> entity, @Nullable String index,
|
||||
@Nullable String type) {
|
||||
IndexCoordinates indexCoordinates = operations.determineIndex(entity, index, type);
|
||||
SearchRequest request = new SearchRequest(indices(query, indexCoordinates::getIndexName));
|
||||
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
|
||||
searchSourceBuilder.query(mappedQuery(query, entity));
|
||||
searchSourceBuilder.version(entity.hasVersionProperty());
|
||||
searchSourceBuilder.trackScores(query.getTrackScores());
|
||||
|
||||
QueryBuilder postFilterQuery = mappedFilterQuery(query, entity);
|
||||
if (postFilterQuery != null) {
|
||||
searchSourceBuilder.postFilter(postFilterQuery);
|
||||
}
|
||||
|
||||
if (query.getSourceFilter() != null) {
|
||||
searchSourceBuilder.fetchSource(query.getSourceFilter().getIncludes(), query.getSourceFilter().getExcludes());
|
||||
}
|
||||
|
||||
if (query instanceof SearchQuery && ((SearchQuery) query).getCollapseBuilder() != null) {
|
||||
searchSourceBuilder.collapse(((SearchQuery) query).getCollapseBuilder());
|
||||
}
|
||||
|
||||
sort(query, entity).forEach(searchSourceBuilder::sort);
|
||||
|
||||
if (query.getMinScore() > 0) {
|
||||
searchSourceBuilder.minScore(query.getMinScore());
|
||||
}
|
||||
|
||||
if (query.getIndicesOptions() != null) {
|
||||
request.indicesOptions(query.getIndicesOptions());
|
||||
}
|
||||
|
||||
if (query.getPreference() != null) {
|
||||
request.preference(query.getPreference());
|
||||
}
|
||||
|
||||
if (query.getSearchType() != null) {
|
||||
request.searchType(query.getSearchType());
|
||||
}
|
||||
|
||||
Pageable pageable = query.getPageable();
|
||||
|
||||
if (pageable.isPaged()) {
|
||||
|
||||
long offset = pageable.getOffset();
|
||||
if (offset > Integer.MAX_VALUE) {
|
||||
throw new IllegalArgumentException(String.format("Offset must not be more than %s", Integer.MAX_VALUE));
|
||||
}
|
||||
|
||||
searchSourceBuilder.from((int) offset);
|
||||
searchSourceBuilder.size(pageable.getPageSize());
|
||||
|
||||
request.source(searchSourceBuilder);
|
||||
} else {
|
||||
request.source(searchSourceBuilder);
|
||||
}
|
||||
return request;
|
||||
}
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.core.ReactiveElasticsearchOperations#count(Query, Class, String, String)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Long> count(Query query, Class<?> entityType, String index, String type) {
|
||||
|
||||
// TODO: ES 7.0 has a dedicated CountRequest - use that one once available.
|
||||
return find(query, entityType, index, type).count();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
@@ -442,6 +511,22 @@ public class ReactiveElasticsearchTemplate implements ReactiveElasticsearchOpera
|
||||
return prepareWriteRequest(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Customization hook to modify a generated {@link SearchRequest} prior to its execution. Eg. by setting the
|
||||
* {@link SearchRequest#indicesOptions(IndicesOptions) indices options} if applicable.
|
||||
*
|
||||
* @param request the generated {@link CountRequest}.
|
||||
* @return never {@literal null}.
|
||||
*/
|
||||
protected CountRequest prepareCountRequest(CountRequest request) {
|
||||
|
||||
if (indicesOptions == null) {
|
||||
return request;
|
||||
}
|
||||
|
||||
return request.indicesOptions(indicesOptions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Customization hook to modify a generated {@link SearchRequest} prior to its execution. Eg. by setting the
|
||||
* {@link SearchRequest#indicesOptions(IndicesOptions) indices options} if applicable.
|
||||
@@ -542,16 +627,32 @@ public class ReactiveElasticsearchTemplate implements ReactiveElasticsearchOpera
|
||||
.onErrorResume(NoSuchIndexException.class, it -> Mono.empty());
|
||||
}
|
||||
|
||||
/**
|
||||
* Customization hook on the actual execution result {@link Publisher}. <br />
|
||||
*
|
||||
* @param request the already prepared {@link CountRequest} ready to be executed.
|
||||
* @return a {@link Mono} emitting the result of the operation.
|
||||
*/
|
||||
protected Mono<Long> doCount(CountRequest request) {
|
||||
|
||||
if (QUERY_LOGGER.isDebugEnabled()) {
|
||||
QUERY_LOGGER.debug("Executing doCount: {}", request);
|
||||
}
|
||||
|
||||
return Mono.from(execute(client -> client.count(request))) //
|
||||
.onErrorResume(NoSuchIndexException.class, it -> Mono.empty());
|
||||
}
|
||||
|
||||
/**
|
||||
* Customization hook on the actual execution result {@link Publisher}. <br />
|
||||
*
|
||||
* @param request the already prepared {@link SearchRequest} ready to be executed.
|
||||
* @return a {@link Flux} emitting the result of the operation.
|
||||
*/
|
||||
protected Flux<SearchHit> doScan(SearchRequest request) {
|
||||
protected Flux<SearchHit> doScroll(SearchRequest request) {
|
||||
|
||||
if (QUERY_LOGGER.isDebugEnabled()) {
|
||||
QUERY_LOGGER.debug("Executing doScan: {}", request);
|
||||
QUERY_LOGGER.debug("Executing doScroll: {}", request);
|
||||
}
|
||||
|
||||
return Flux.from(execute(client -> client.scroll(request))) //
|
||||
@@ -650,9 +751,7 @@ public class ReactiveElasticsearchTemplate implements ReactiveElasticsearchOpera
|
||||
elasticsearchQuery = new WrapperQueryBuilder(((StringQuery) query).getSource());
|
||||
} else if (query instanceof NativeSearchQuery) {
|
||||
elasticsearchQuery = ((NativeSearchQuery) query).getQuery();
|
||||
}
|
||||
|
||||
else {
|
||||
} else {
|
||||
throw new IllegalArgumentException(String.format("Unknown query type '%s'.", query.getClass()));
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright 2019 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.InputStream;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.util.StreamUtils;
|
||||
|
||||
/**
|
||||
* Utility to read {@link org.springframework.core.io.Resource}s.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 3.2
|
||||
*/
|
||||
abstract class ResourceUtil {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(ResourceUtil.class);
|
||||
|
||||
/**
|
||||
* Read a {@link ClassPathResource} into a {@link String}.
|
||||
*
|
||||
* @param url
|
||||
* @return
|
||||
*/
|
||||
public static String readFileFromClasspath(String url) {
|
||||
|
||||
ClassPathResource classPathResource = new ClassPathResource(url);
|
||||
try (InputStream is = classPathResource.getInputStream()) {
|
||||
return StreamUtils.copyToString(is, Charset.defaultCharset());
|
||||
} catch (Exception e) {
|
||||
LOGGER.debug(String.format("Failed to load file from url: %s: %s", url, e.getMessage()));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Utility constructor
|
||||
private ResourceUtil() {}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright 2019 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.Iterator;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.springframework.data.util.CloseableIterator;
|
||||
|
||||
/**
|
||||
* Utility to support streaming queries.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 3.2
|
||||
*/
|
||||
abstract class StreamQueries {
|
||||
|
||||
/**
|
||||
* Stream query results using {@link ScrolledPage}.
|
||||
*
|
||||
* @param page the initial page.
|
||||
* @param continueFunction continuation function accepting the current scrollId.
|
||||
* @param clearScroll cleanup function accepting the current scrollId.
|
||||
* @param <T>
|
||||
* @return the {@link CloseableIterator}.
|
||||
*/
|
||||
static <T> CloseableIterator<T> streamResults(ScrolledPage<T> page,
|
||||
Function<String, ScrolledPage<T>> continueFunction, Consumer<String> clearScroll) {
|
||||
|
||||
return new CloseableIterator<T>() {
|
||||
|
||||
/** As we couldn't retrieve single result with scroll, store current hits. */
|
||||
private volatile Iterator<T> currentHits = page.iterator();
|
||||
|
||||
/** The scroll id. */
|
||||
private volatile String scrollId = page.getScrollId();
|
||||
|
||||
/** If stream is finished (ie: cluster returns no results. */
|
||||
private volatile boolean finished = !currentHits.hasNext();
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
try {
|
||||
// Clear scroll on cluster only in case of error (cause elasticsearch auto clear scroll when it's done)
|
||||
if (!finished && scrollId != null && currentHits != null && currentHits.hasNext()) {
|
||||
clearScroll.accept(scrollId);
|
||||
}
|
||||
} finally {
|
||||
currentHits = null;
|
||||
scrollId = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
// Test if stream is finished
|
||||
if (finished) {
|
||||
return false;
|
||||
}
|
||||
// Test if it remains hits
|
||||
if (currentHits == null || !currentHits.hasNext()) {
|
||||
// Do a new request
|
||||
ScrolledPage<T> scroll = continueFunction.apply(scrollId);
|
||||
// Save hits and scroll id
|
||||
currentHits = scroll.iterator();
|
||||
finished = !currentHits.hasNext();
|
||||
scrollId = scroll.getScrollId();
|
||||
}
|
||||
return currentHits.hasNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public T next() {
|
||||
if (hasNext()) {
|
||||
return currentHits.next();
|
||||
}
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException("remove");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// utility constructor
|
||||
private StreamQueries() {}
|
||||
}
|
||||
+1
-1
@@ -39,7 +39,7 @@ public class SimpleElasticsearchMappingContext
|
||||
|
||||
@Override
|
||||
protected <T> SimpleElasticsearchPersistentEntity<?> createPersistentEntity(TypeInformation<T> typeInformation) {
|
||||
final SimpleElasticsearchPersistentEntity<T> persistentEntity = new SimpleElasticsearchPersistentEntity<>(
|
||||
SimpleElasticsearchPersistentEntity<T> persistentEntity = new SimpleElasticsearchPersistentEntity<>(
|
||||
typeInformation);
|
||||
if (context != null) {
|
||||
persistentEntity.setApplicationContext(context);
|
||||
|
||||
@@ -35,6 +35,7 @@ import org.springframework.util.Assert;
|
||||
* @author Mark Paluch
|
||||
* @author Alen Turkovic
|
||||
* @author Sascha Woo
|
||||
* @author Farid Azaza
|
||||
*/
|
||||
abstract class AbstractQuery implements Query {
|
||||
|
||||
@@ -50,6 +51,7 @@ abstract class AbstractQuery implements Query {
|
||||
protected SearchType searchType = SearchType.DFS_QUERY_THEN_FETCH;
|
||||
protected IndicesOptions indicesOptions;
|
||||
protected boolean trackScores;
|
||||
protected String preference;
|
||||
|
||||
@Override
|
||||
public Sort getSort() {
|
||||
@@ -63,9 +65,9 @@ abstract class AbstractQuery implements Query {
|
||||
|
||||
@Override
|
||||
public final <T extends Query> T setPageable(Pageable pageable) {
|
||||
|
||||
|
||||
Assert.notNull(pageable, "Pageable must not be null!");
|
||||
|
||||
|
||||
this.pageable = pageable;
|
||||
return (T) this.addSort(pageable.getSort());
|
||||
}
|
||||
@@ -183,4 +185,14 @@ abstract class AbstractQuery implements Query {
|
||||
public void setTrackScores(boolean trackScores) {
|
||||
this.trackScores = trackScores;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPreference() {
|
||||
return preference;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPreference(String preference) {
|
||||
this.preference = preference;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
* Copyright 2019 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.query;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.elasticsearch.action.support.ActiveShardCount;
|
||||
import org.elasticsearch.action.support.WriteRequest;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Options that may be passed to an
|
||||
* {@link org.springframework.data.elasticsearch.core.ElasticsearchOperations#bulkIndex(List, BulkOptions)} or
|
||||
* {@link org.springframework.data.elasticsearch.core.ElasticsearchOperations#bulkUpdate(List, BulkOptions)} call. <br/>
|
||||
* Use {@link BulkOptions#builder()} to obtain a builder, then set the desired properties and call
|
||||
* {@link BulkOptionsBuilder#build()} to get the BulkOptions object.
|
||||
*
|
||||
* @author Peter-Josef Meisch
|
||||
* @author Mark Paluch
|
||||
* @since 3.2
|
||||
*/
|
||||
public class BulkOptions {
|
||||
|
||||
private static final BulkOptions defaultOptions = builder().build();
|
||||
|
||||
private final @Nullable TimeValue timeout;
|
||||
private final @Nullable WriteRequest.RefreshPolicy refreshPolicy;
|
||||
private final @Nullable ActiveShardCount waitForActiveShards;
|
||||
private final @Nullable String pipeline;
|
||||
private final @Nullable String routingId;
|
||||
|
||||
private BulkOptions(@Nullable TimeValue timeout, @Nullable WriteRequest.RefreshPolicy refreshPolicy,
|
||||
@Nullable ActiveShardCount waitForActiveShards, @Nullable String pipeline, @Nullable String routingId) {
|
||||
this.timeout = timeout;
|
||||
this.refreshPolicy = refreshPolicy;
|
||||
this.waitForActiveShards = waitForActiveShards;
|
||||
this.pipeline = pipeline;
|
||||
this.routingId = routingId;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public TimeValue getTimeout() {
|
||||
return timeout;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public WriteRequest.RefreshPolicy getRefreshPolicy() {
|
||||
return refreshPolicy;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public ActiveShardCount getWaitForActiveShards() {
|
||||
return waitForActiveShards;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getPipeline() {
|
||||
return pipeline;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getRoutingId() {
|
||||
return routingId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link BulkOptionsBuilder} to build {@link BulkOptions}.
|
||||
*
|
||||
* @return a new {@link BulkOptionsBuilder} to build {@link BulkOptions}.
|
||||
*/
|
||||
public static BulkOptionsBuilder builder() {
|
||||
return new BulkOptionsBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return default {@link BulkOptions}.
|
||||
*
|
||||
* @return default {@link BulkOptions}.
|
||||
*/
|
||||
public static BulkOptions defaultOptions() {
|
||||
return defaultOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder for {@link BulkOptions}.
|
||||
*/
|
||||
public static class BulkOptionsBuilder {
|
||||
|
||||
private @Nullable TimeValue timeout;
|
||||
private @Nullable WriteRequest.RefreshPolicy refreshPolicy;
|
||||
private @Nullable ActiveShardCount waitForActiveShards;
|
||||
private @Nullable String pipeline;
|
||||
private @Nullable String routingId;
|
||||
|
||||
private BulkOptionsBuilder() {}
|
||||
|
||||
public BulkOptionsBuilder withTimeout(TimeValue timeout) {
|
||||
this.timeout = timeout;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BulkOptionsBuilder withRefreshPolicy(WriteRequest.RefreshPolicy refreshPolicy) {
|
||||
this.refreshPolicy = refreshPolicy;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BulkOptionsBuilder withWaitForActiveShards(ActiveShardCount waitForActiveShards) {
|
||||
this.waitForActiveShards = waitForActiveShards;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BulkOptionsBuilder withPipeline(String pipeline) {
|
||||
this.pipeline = pipeline;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BulkOptionsBuilder withRoutingId(String routingId) {
|
||||
this.routingId = routingId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BulkOptions build() {
|
||||
return new BulkOptions(timeout, refreshPolicy, waitForActiveShards, pipeline, routingId);
|
||||
}
|
||||
}
|
||||
}
|
||||
+35
-14
@@ -16,16 +16,16 @@
|
||||
package org.springframework.data.elasticsearch.core.query;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.elasticsearch.index.query.QueryBuilder;
|
||||
import org.elasticsearch.search.aggregations.AbstractAggregationBuilder;
|
||||
import org.elasticsearch.search.collapse.CollapseBuilder;
|
||||
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
|
||||
import org.elasticsearch.search.sort.SortBuilder;
|
||||
import org.springframework.data.elasticsearch.core.facet.FacetRequest;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* NativeSearchQuery
|
||||
*
|
||||
@@ -33,36 +33,42 @@ import java.util.Arrays;
|
||||
* @author Mohsin Husen
|
||||
* @author Artur Konczak
|
||||
* @author Jean-Baptiste Nizet
|
||||
* @author Martin Choraine
|
||||
*/
|
||||
public class NativeSearchQuery extends AbstractQuery implements SearchQuery {
|
||||
|
||||
private QueryBuilder query;
|
||||
private QueryBuilder filter;
|
||||
private List<SortBuilder> sorts;
|
||||
private final List<ScriptField> scriptFields = new ArrayList<>();
|
||||
private final List<ScriptField> scriptFields = new ArrayList<>();
|
||||
private CollapseBuilder collapseBuilder;
|
||||
private List<FacetRequest> facets;
|
||||
private List<AbstractAggregationBuilder> aggregations;
|
||||
private HighlightBuilder highlightBuilder;
|
||||
private HighlightBuilder.Field[] highlightFields;
|
||||
private List<IndexBoost> indicesBoost;
|
||||
|
||||
|
||||
public NativeSearchQuery(QueryBuilder query) {
|
||||
|
||||
this.query = query;
|
||||
}
|
||||
|
||||
public NativeSearchQuery(QueryBuilder query, QueryBuilder filter) {
|
||||
|
||||
this.query = query;
|
||||
this.filter = filter;
|
||||
}
|
||||
|
||||
public NativeSearchQuery(QueryBuilder query, QueryBuilder filter, List<SortBuilder> sorts) {
|
||||
|
||||
this.query = query;
|
||||
this.filter = filter;
|
||||
this.sorts = sorts;
|
||||
}
|
||||
|
||||
public NativeSearchQuery(QueryBuilder query, QueryBuilder filter, List<SortBuilder> sorts, HighlightBuilder.Field[] highlightFields) {
|
||||
public NativeSearchQuery(QueryBuilder query, QueryBuilder filter, List<SortBuilder> sorts,
|
||||
HighlightBuilder.Field[] highlightFields) {
|
||||
|
||||
this.query = query;
|
||||
this.filter = filter;
|
||||
this.sorts = sorts;
|
||||
@@ -71,6 +77,7 @@ public class NativeSearchQuery extends AbstractQuery implements SearchQuery {
|
||||
|
||||
public NativeSearchQuery(QueryBuilder query, QueryBuilder filter, List<SortBuilder> sorts,
|
||||
HighlightBuilder highlighBuilder, HighlightBuilder.Field[] highlightFields) {
|
||||
|
||||
this.query = query;
|
||||
this.filter = filter;
|
||||
this.sorts = sorts;
|
||||
@@ -100,21 +107,34 @@ public class NativeSearchQuery extends AbstractQuery implements SearchQuery {
|
||||
return highlightFields;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ScriptField> getScriptFields() { return scriptFields; }
|
||||
@Override
|
||||
public List<ScriptField> getScriptFields() {
|
||||
return scriptFields;
|
||||
}
|
||||
|
||||
public void setScriptFields(List<ScriptField> scriptFields) {
|
||||
this.scriptFields.addAll(scriptFields);
|
||||
}
|
||||
public void setScriptFields(List<ScriptField> scriptFields) {
|
||||
this.scriptFields.addAll(scriptFields);
|
||||
}
|
||||
|
||||
public void addScriptField(ScriptField... scriptField) {
|
||||
scriptFields.addAll(Arrays.asList(scriptField));
|
||||
}
|
||||
public void addScriptField(ScriptField... scriptField) {
|
||||
scriptFields.addAll(Arrays.asList(scriptField));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CollapseBuilder getCollapseBuilder() {
|
||||
return collapseBuilder;
|
||||
}
|
||||
|
||||
public void setCollapseBuilder(CollapseBuilder collapseBuilder) {
|
||||
this.collapseBuilder = collapseBuilder;
|
||||
}
|
||||
|
||||
public void addFacet(FacetRequest facetRequest) {
|
||||
|
||||
if (facets == null) {
|
||||
facets = new ArrayList<>();
|
||||
}
|
||||
|
||||
facets.add(facetRequest);
|
||||
}
|
||||
|
||||
@@ -132,11 +152,12 @@ public class NativeSearchQuery extends AbstractQuery implements SearchQuery {
|
||||
return aggregations;
|
||||
}
|
||||
|
||||
|
||||
public void addAggregation(AbstractAggregationBuilder aggregationBuilder) {
|
||||
|
||||
if (aggregations == null) {
|
||||
aggregations = new ArrayList<>();
|
||||
}
|
||||
|
||||
aggregations.add(aggregationBuilder);
|
||||
}
|
||||
|
||||
|
||||
+25
-1
@@ -15,14 +15,17 @@
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.core.query;
|
||||
|
||||
import static org.springframework.util.CollectionUtils.isEmpty;
|
||||
import static org.springframework.util.CollectionUtils.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.elasticsearch.action.search.SearchType;
|
||||
import org.elasticsearch.action.support.IndicesOptions;
|
||||
import org.elasticsearch.index.query.QueryBuilder;
|
||||
import org.elasticsearch.search.aggregations.AbstractAggregationBuilder;
|
||||
import org.elasticsearch.search.collapse.CollapseBuilder;
|
||||
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
|
||||
import org.elasticsearch.search.sort.SortBuilder;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
@@ -38,6 +41,8 @@ import org.springframework.data.elasticsearch.core.facet.FacetRequest;
|
||||
* @author Alen Turkovic
|
||||
* @author Sascha Woo
|
||||
* @author Jean-Baptiste Nizet
|
||||
* @author Martin Choraine
|
||||
* @author Farid Azaza
|
||||
*/
|
||||
public class NativeSearchQueryBuilder {
|
||||
|
||||
@@ -54,6 +59,7 @@ public class NativeSearchQueryBuilder {
|
||||
private String[] types;
|
||||
private String[] fields;
|
||||
private SourceFilter sourceFilter;
|
||||
private CollapseBuilder collapseBuilder;
|
||||
private List<IndexBoost> indicesBoost;
|
||||
private float minScore;
|
||||
private boolean trackScores;
|
||||
@@ -61,6 +67,7 @@ public class NativeSearchQueryBuilder {
|
||||
private String route;
|
||||
private SearchType searchType;
|
||||
private IndicesOptions indicesOptions;
|
||||
private String preference;
|
||||
|
||||
public NativeSearchQueryBuilder withQuery(QueryBuilder queryBuilder) {
|
||||
this.queryBuilder = queryBuilder;
|
||||
@@ -82,6 +89,11 @@ public class NativeSearchQueryBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
public NativeSearchQueryBuilder withCollapseField(String collapseField) {
|
||||
this.collapseBuilder = new CollapseBuilder(collapseField);
|
||||
return this;
|
||||
}
|
||||
|
||||
public NativeSearchQueryBuilder addAggregation(AbstractAggregationBuilder aggregationBuilder) {
|
||||
this.aggregationBuilders.add(aggregationBuilder);
|
||||
return this;
|
||||
@@ -167,6 +179,11 @@ public class NativeSearchQueryBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
public NativeSearchQueryBuilder withPreference(String preference) {
|
||||
this.preference = preference;
|
||||
return this;
|
||||
}
|
||||
|
||||
public NativeSearchQuery build() {
|
||||
NativeSearchQuery nativeSearchQuery = new NativeSearchQuery(queryBuilder, filterBuilder, sortBuilders,
|
||||
highlightBuilder, highlightFields);
|
||||
@@ -198,6 +215,10 @@ public class NativeSearchQueryBuilder {
|
||||
nativeSearchQuery.setScriptFields(scriptFields);
|
||||
}
|
||||
|
||||
if (collapseBuilder != null) {
|
||||
nativeSearchQuery.setCollapseBuilder(collapseBuilder);
|
||||
}
|
||||
|
||||
if (!isEmpty(facetRequests)) {
|
||||
nativeSearchQuery.setFacets(facetRequests);
|
||||
}
|
||||
@@ -225,6 +246,9 @@ public class NativeSearchQueryBuilder {
|
||||
if (indicesOptions != null) {
|
||||
nativeSearchQuery.setIndicesOptions(indicesOptions);
|
||||
}
|
||||
if (preference != null) {
|
||||
nativeSearchQuery.setPreference(preference);
|
||||
}
|
||||
|
||||
return nativeSearchQuery;
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.core.query;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
@@ -35,6 +34,7 @@ import org.springframework.data.domain.Sort;
|
||||
* @author Alen Turkovic
|
||||
* @author Sascha Woo
|
||||
* @author Christoph Strobl
|
||||
* @author Farid Azaza
|
||||
*/
|
||||
public interface Query {
|
||||
|
||||
@@ -178,4 +178,20 @@ public interface Query {
|
||||
* @return null if not set
|
||||
*/
|
||||
IndicesOptions getIndicesOptions();
|
||||
|
||||
/**
|
||||
* Get preference
|
||||
*
|
||||
* @return
|
||||
* @since 3.2
|
||||
*/
|
||||
String getPreference();
|
||||
|
||||
/**
|
||||
* Add preference filter to be added as part of search request
|
||||
*
|
||||
* @param preference
|
||||
* @since 3.2
|
||||
*/
|
||||
void setPreference(String preference);
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import java.util.List;
|
||||
|
||||
import org.elasticsearch.index.query.QueryBuilder;
|
||||
import org.elasticsearch.search.aggregations.AbstractAggregationBuilder;
|
||||
import org.elasticsearch.search.collapse.CollapseBuilder;
|
||||
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
|
||||
import org.elasticsearch.search.sort.SortBuilder;
|
||||
import org.springframework.data.elasticsearch.core.facet.FacetRequest;
|
||||
@@ -30,6 +31,7 @@ import org.springframework.data.elasticsearch.core.facet.FacetRequest;
|
||||
* @author Mohsin Husen
|
||||
* @author Artur Konczak
|
||||
* @author Jean-Baptiste Nizet
|
||||
* @author Martin Choraine
|
||||
*/
|
||||
public interface SearchQuery extends Query {
|
||||
|
||||
@@ -50,6 +52,8 @@ public interface SearchQuery extends Query {
|
||||
|
||||
List<IndexBoost> getIndicesBoost();
|
||||
|
||||
List<ScriptField> getScriptFields();
|
||||
List<ScriptField> getScriptFields();
|
||||
|
||||
CollapseBuilder getCollapseBuilder();
|
||||
|
||||
}
|
||||
|
||||
+8
@@ -27,12 +27,20 @@ import org.springframework.data.repository.NoRepositoryBean;
|
||||
* @author Rizwan Idrees
|
||||
* @author Mohsin Husen
|
||||
* @author Sascha Woo
|
||||
* @author Murali Chevuri
|
||||
*/
|
||||
@NoRepositoryBean
|
||||
public interface ElasticsearchRepository<T, ID> extends ElasticsearchCrudRepository<T, ID> {
|
||||
|
||||
<S extends T> S index(S entity);
|
||||
|
||||
/**
|
||||
* This method is intended to be used when many single inserts must be made that cannot be aggregated to be inserted
|
||||
* with {@link #saveAll(Iterable)}. This might lead to a temporary inconsistent state until {@link #refresh()} is
|
||||
* called.
|
||||
*/
|
||||
<S extends T> S indexWithoutRefresh(S entity);
|
||||
|
||||
Iterable<T> search(QueryBuilder query);
|
||||
|
||||
Page<T> search(QueryBuilder query, Pageable pageable);
|
||||
|
||||
+7
-1
@@ -25,6 +25,7 @@ import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @author Peter-Josef Meisch
|
||||
* @since 3.2
|
||||
*/
|
||||
public class ConvertingParameterAccessor implements ElasticsearchParameterAccessor {
|
||||
@@ -58,7 +59,12 @@ public class ConvertingParameterAccessor implements ElasticsearchParameterAccess
|
||||
return delegate.getDynamicProjection();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Override
|
||||
public Class<?> findDynamicProjection() {
|
||||
return delegate.findDynamicProjection();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getBindableValue(int index) {
|
||||
return getConvertedValue(delegate.getBindableValue(index));
|
||||
}
|
||||
|
||||
+25
-13
@@ -16,7 +16,6 @@
|
||||
package org.springframework.data.elasticsearch.repository.query;
|
||||
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
|
||||
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentProperty;
|
||||
import org.springframework.data.elasticsearch.core.query.CriteriaQuery;
|
||||
@@ -24,9 +23,10 @@ import org.springframework.data.elasticsearch.repository.query.parser.Elasticsea
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.repository.query.ParametersParameterAccessor;
|
||||
import org.springframework.data.repository.query.parser.PartTree;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.data.util.CloseableIterator;
|
||||
import org.springframework.data.util.StreamUtils;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* ElasticsearchPartQuery
|
||||
@@ -35,9 +35,13 @@ import org.springframework.data.util.StreamUtils;
|
||||
* @author Mohsin Husen
|
||||
* @author Kevin Leturc
|
||||
* @author Mark Paluch
|
||||
* @author Rasmus Faber-Espensen
|
||||
* @author Peter-Josef Meisch
|
||||
*/
|
||||
public class ElasticsearchPartQuery extends AbstractElasticsearchRepositoryQuery {
|
||||
|
||||
private static final int DEFAULT_STREAM_BATCH_SIZE = 500;
|
||||
|
||||
private final PartTree tree;
|
||||
private final MappingContext<?, ElasticsearchPersistentProperty> mappingContext;
|
||||
|
||||
@@ -49,9 +53,12 @@ public class ElasticsearchPartQuery extends AbstractElasticsearchRepositoryQuery
|
||||
|
||||
@Override
|
||||
public Object execute(Object[] parameters) {
|
||||
|
||||
ParametersParameterAccessor accessor = new ParametersParameterAccessor(queryMethod.getParameters(), parameters);
|
||||
CriteriaQuery query = createQuery(accessor);
|
||||
if(tree.isDelete()) {
|
||||
Assert.notNull(query, "unsupported query");
|
||||
|
||||
if (tree.isDelete()) {
|
||||
Object result = countOrGetDocumentsForDelete(query, accessor);
|
||||
elasticsearchOperations.delete(query, queryMethod.getEntityInformation().getJavaType());
|
||||
return result;
|
||||
@@ -60,24 +67,28 @@ public class ElasticsearchPartQuery extends AbstractElasticsearchRepositoryQuery
|
||||
return elasticsearchOperations.queryForPage(query, queryMethod.getEntityInformation().getJavaType());
|
||||
} else if (queryMethod.isStreamQuery()) {
|
||||
Class<?> entityType = queryMethod.getEntityInformation().getJavaType();
|
||||
if (query.getPageable().isUnpaged()) {
|
||||
int itemCount = (int) elasticsearchOperations.count(query, queryMethod.getEntityInformation().getJavaType());
|
||||
query.setPageable(PageRequest.of(0, Math.max(1, itemCount)));
|
||||
}
|
||||
|
||||
return StreamUtils.createStreamFromIterator((CloseableIterator<Object>) elasticsearchOperations.stream(query, entityType));
|
||||
|
||||
} else if (queryMethod.isCollectionQuery()) {
|
||||
if (accessor.getPageable().isUnpaged()) {
|
||||
query.setPageable(PageRequest.of(0, DEFAULT_STREAM_BATCH_SIZE));
|
||||
} else {
|
||||
query.setPageable(accessor.getPageable());
|
||||
}
|
||||
return StreamUtils
|
||||
.createStreamFromIterator((CloseableIterator<Object>) elasticsearchOperations.stream(query, entityType));
|
||||
} else if (queryMethod.isCollectionQuery()) {
|
||||
|
||||
if (accessor.getPageable().isUnpaged()) {
|
||||
|
||||
int itemCount = (int) elasticsearchOperations.count(query, queryMethod.getEntityInformation().getJavaType());
|
||||
query.setPageable(PageRequest.of(0, Math.max(1, itemCount)));
|
||||
} else {
|
||||
query.setPageable(accessor.getPageable());
|
||||
}
|
||||
query.setPageable(accessor.getPageable());
|
||||
}
|
||||
|
||||
return elasticsearchOperations.queryForList(query, queryMethod.getEntityInformation().getJavaType());
|
||||
} else if (tree.isCountProjection()) {
|
||||
return elasticsearchOperations.count(query, queryMethod.getEntityInformation().getJavaType());
|
||||
}
|
||||
|
||||
return elasticsearchOperations.queryForObject(query, queryMethod.getEntityInformation().getJavaType());
|
||||
}
|
||||
|
||||
@@ -86,6 +97,7 @@ public class ElasticsearchPartQuery extends AbstractElasticsearchRepositoryQuery
|
||||
Object result = null;
|
||||
|
||||
if (queryMethod.isCollectionQuery()) {
|
||||
|
||||
if (accessor.getPageable().isUnpaged()) {
|
||||
int itemCount = (int) elasticsearchOperations.count(query, queryMethod.getEntityInformation().getJavaType());
|
||||
query.setPageable(PageRequest.of(0, Math.max(1, itemCount)));
|
||||
|
||||
+9
-4
@@ -34,6 +34,7 @@ import org.springframework.data.repository.query.ParameterAccessor;
|
||||
import org.springframework.data.repository.query.parser.AbstractQueryCreator;
|
||||
import org.springframework.data.repository.query.parser.Part;
|
||||
import org.springframework.data.repository.query.parser.PartTree;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* ElasticsearchQueryCreator
|
||||
@@ -42,13 +43,14 @@ import org.springframework.data.repository.query.parser.PartTree;
|
||||
* @author Mohsin Husen
|
||||
* @author Franck Marchand
|
||||
* @author Artur Konczak
|
||||
* @author Peter-Josef Meisch
|
||||
*/
|
||||
public class ElasticsearchQueryCreator extends AbstractQueryCreator<CriteriaQuery, CriteriaQuery> {
|
||||
|
||||
private final MappingContext<?, ElasticsearchPersistentProperty> context;
|
||||
|
||||
public ElasticsearchQueryCreator(PartTree tree, ParameterAccessor parameters,
|
||||
MappingContext<?, ElasticsearchPersistentProperty> context) {
|
||||
MappingContext<?, ElasticsearchPersistentProperty> context) {
|
||||
super(tree, parameters);
|
||||
this.context = context;
|
||||
}
|
||||
@@ -83,9 +85,12 @@ public class ElasticsearchQueryCreator extends AbstractQueryCreator<CriteriaQuer
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CriteriaQuery complete(CriteriaQuery query, Sort sort) {
|
||||
protected CriteriaQuery complete(@Nullable CriteriaQuery query, Sort sort) {
|
||||
|
||||
if (query == null) {
|
||||
return null;
|
||||
|
||||
// this is the case in a findAllByOrderByField method, add empty criteria
|
||||
query = new CriteriaQuery(new Criteria());
|
||||
}
|
||||
return query.addSort(sort);
|
||||
}
|
||||
@@ -190,6 +195,6 @@ public class ElasticsearchQueryCreator extends AbstractQueryCreator<CriteriaQuer
|
||||
} else if (o.getClass().isArray()) {
|
||||
return (Object[]) o;
|
||||
}
|
||||
return new Object[]{o};
|
||||
return new Object[] { o };
|
||||
}
|
||||
}
|
||||
|
||||
+71
-3
@@ -56,11 +56,12 @@ import org.springframework.util.Assert;
|
||||
* @author Christoph Strobl
|
||||
* @author Michael Wirth
|
||||
* @author Sascha Woo
|
||||
* @author Murali Chevuri
|
||||
*/
|
||||
public abstract class AbstractElasticsearchRepository<T, ID>
|
||||
implements ElasticsearchRepository<T, ID> {
|
||||
public abstract class AbstractElasticsearchRepository<T, ID> implements ElasticsearchRepository<T, ID> {
|
||||
|
||||
static final Logger LOGGER = LoggerFactory.getLogger(AbstractElasticsearchRepository.class);
|
||||
|
||||
protected ElasticsearchOperations elasticsearchOperations;
|
||||
protected Class<T> entityClass;
|
||||
protected ElasticsearchEntityInformation<T, ID> entityInformation;
|
||||
@@ -76,6 +77,7 @@ public abstract class AbstractElasticsearchRepository<T, ID>
|
||||
|
||||
public AbstractElasticsearchRepository(ElasticsearchEntityInformation<T, ID> metadata,
|
||||
ElasticsearchOperations elasticsearchOperations) {
|
||||
|
||||
this(elasticsearchOperations);
|
||||
|
||||
Assert.notNull(metadata, "ElasticsearchEntityInformation must not be null!");
|
||||
@@ -88,24 +90,28 @@ public abstract class AbstractElasticsearchRepository<T, ID>
|
||||
putMapping();
|
||||
}
|
||||
} catch (ElasticsearchException exception) {
|
||||
LOGGER.error("failed to load elasticsearch nodes : " + exception.getDetailedMessage());
|
||||
LOGGER.error("failed to load elasticsearch nodes : {}", exception.getDetailedMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void createIndex() {
|
||||
|
||||
elasticsearchOperations.createIndex(getEntityClass());
|
||||
}
|
||||
|
||||
private void putMapping() {
|
||||
|
||||
elasticsearchOperations.putMapping(getEntityClass());
|
||||
}
|
||||
|
||||
private boolean createIndexAndMapping() {
|
||||
|
||||
return elasticsearchOperations.getPersistentEntityFor(getEntityClass()).isCreateIndexAndMapping();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<T> findById(ID id) {
|
||||
|
||||
GetQuery query = new GetQuery();
|
||||
query.setId(stringIdRepresentation(id));
|
||||
return Optional.ofNullable(elasticsearchOperations.queryForObject(query, getEntityClass()));
|
||||
@@ -113,123 +119,167 @@ public abstract class AbstractElasticsearchRepository<T, ID>
|
||||
|
||||
@Override
|
||||
public Iterable<T> findAll() {
|
||||
|
||||
int itemCount = (int) this.count();
|
||||
if (itemCount == 0) {
|
||||
return new PageImpl<>(Collections.<T> emptyList());
|
||||
}
|
||||
|
||||
return this.findAll(PageRequest.of(0, Math.max(1, itemCount)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<T> findAll(Pageable pageable) {
|
||||
|
||||
SearchQuery query = new NativeSearchQueryBuilder().withQuery(matchAllQuery()).withPageable(pageable).build();
|
||||
|
||||
return elasticsearchOperations.queryForPage(query, getEntityClass());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<T> findAll(Sort sort) {
|
||||
|
||||
int itemCount = (int) this.count();
|
||||
if (itemCount == 0) {
|
||||
return new PageImpl<>(Collections.<T> emptyList());
|
||||
}
|
||||
|
||||
SearchQuery query = new NativeSearchQueryBuilder().withQuery(matchAllQuery())
|
||||
.withPageable(PageRequest.of(0, itemCount, sort)).build();
|
||||
|
||||
return elasticsearchOperations.queryForPage(query, getEntityClass());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<T> findAllById(Iterable<ID> ids) {
|
||||
|
||||
Assert.notNull(ids, "ids can't be null.");
|
||||
|
||||
SearchQuery query = new NativeSearchQueryBuilder().withIds(stringIdsRepresentation(ids)).build();
|
||||
|
||||
return elasticsearchOperations.multiGet(query, getEntityClass());
|
||||
}
|
||||
|
||||
@Override
|
||||
public long count() {
|
||||
|
||||
SearchQuery query = new NativeSearchQueryBuilder().withQuery(matchAllQuery()).build();
|
||||
|
||||
return elasticsearchOperations.count(query, getEntityClass());
|
||||
}
|
||||
|
||||
@Override
|
||||
public <S extends T> S save(S entity) {
|
||||
|
||||
Assert.notNull(entity, "Cannot save 'null' entity.");
|
||||
|
||||
elasticsearchOperations.index(createIndexQuery(entity));
|
||||
elasticsearchOperations.refresh(entityInformation.getIndexName());
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
public <S extends T> List<S> save(List<S> entities) {
|
||||
|
||||
Assert.notNull(entities, "Cannot insert 'null' as a List.");
|
||||
Assert.notEmpty(entities, "Cannot insert empty List.");
|
||||
|
||||
List<IndexQuery> queries = new ArrayList<>();
|
||||
for (S s : entities) {
|
||||
queries.add(createIndexQuery(s));
|
||||
}
|
||||
elasticsearchOperations.bulkIndex(queries);
|
||||
elasticsearchOperations.refresh(entityInformation.getIndexName());
|
||||
|
||||
return entities;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <S extends T> S index(S entity) {
|
||||
|
||||
return save(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <S extends T> S indexWithoutRefresh(S entity) {
|
||||
|
||||
Assert.notNull(entity, "Cannot save 'null' entity.");
|
||||
|
||||
elasticsearchOperations.index(createIndexQuery(entity));
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <S extends T> Iterable<S> saveAll(Iterable<S> entities) {
|
||||
|
||||
Assert.notNull(entities, "Cannot insert 'null' as a List.");
|
||||
|
||||
List<IndexQuery> queries = new ArrayList<>();
|
||||
for (S s : entities) {
|
||||
queries.add(createIndexQuery(s));
|
||||
}
|
||||
elasticsearchOperations.bulkIndex(queries);
|
||||
elasticsearchOperations.refresh(entityInformation.getIndexName());
|
||||
|
||||
return entities;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean existsById(ID id) {
|
||||
|
||||
return findById(id).isPresent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<T> search(QueryBuilder query) {
|
||||
|
||||
SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(query).build();
|
||||
int count = (int) elasticsearchOperations.count(searchQuery, getEntityClass());
|
||||
if (count == 0) {
|
||||
return new PageImpl<>(Collections.<T> emptyList());
|
||||
}
|
||||
|
||||
searchQuery.setPageable(PageRequest.of(0, count));
|
||||
|
||||
return elasticsearchOperations.queryForPage(searchQuery, getEntityClass());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<T> search(QueryBuilder query, Pageable pageable) {
|
||||
|
||||
SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(query).withPageable(pageable).build();
|
||||
|
||||
return elasticsearchOperations.queryForPage(searchQuery, getEntityClass());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<T> search(SearchQuery query) {
|
||||
|
||||
return elasticsearchOperations.queryForPage(query, getEntityClass());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<T> searchSimilar(T entity, String[] fields, Pageable pageable) {
|
||||
|
||||
Assert.notNull(entity, "Cannot search similar records for 'null'.");
|
||||
Assert.notNull(pageable, "'pageable' cannot be 'null'");
|
||||
|
||||
MoreLikeThisQuery query = new MoreLikeThisQuery();
|
||||
query.setId(stringIdRepresentation(extractIdFromBean(entity)));
|
||||
query.setPageable(pageable);
|
||||
if (fields != null) {
|
||||
query.addFields(fields);
|
||||
}
|
||||
|
||||
return elasticsearchOperations.moreLikeThis(query, getEntityClass());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteById(ID id) {
|
||||
|
||||
Assert.notNull(id, "Cannot delete entity with id 'null'.");
|
||||
|
||||
elasticsearchOperations.delete(entityInformation.getIndexName(), entityInformation.getType(),
|
||||
stringIdRepresentation(id));
|
||||
elasticsearchOperations.refresh(entityInformation.getIndexName());
|
||||
@@ -237,13 +287,16 @@ public abstract class AbstractElasticsearchRepository<T, ID>
|
||||
|
||||
@Override
|
||||
public void delete(T entity) {
|
||||
|
||||
Assert.notNull(entity, "Cannot delete 'null' entity.");
|
||||
|
||||
deleteById(extractIdFromBean(entity));
|
||||
elasticsearchOperations.refresh(entityInformation.getIndexName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteAll(Iterable<? extends T> entities) {
|
||||
|
||||
Assert.notNull(entities, "Cannot delete 'null' list.");
|
||||
for (T entity : entities) {
|
||||
delete(entity);
|
||||
@@ -252,6 +305,7 @@ public abstract class AbstractElasticsearchRepository<T, ID>
|
||||
|
||||
@Override
|
||||
public void deleteAll() {
|
||||
|
||||
DeleteQuery deleteQuery = new DeleteQuery();
|
||||
deleteQuery.setQuery(matchAllQuery());
|
||||
elasticsearchOperations.delete(deleteQuery, getEntityClass());
|
||||
@@ -260,10 +314,12 @@ public abstract class AbstractElasticsearchRepository<T, ID>
|
||||
|
||||
@Override
|
||||
public void refresh() {
|
||||
|
||||
elasticsearchOperations.refresh(getEntityClass());
|
||||
}
|
||||
|
||||
private IndexQuery createIndexQuery(T entity) {
|
||||
|
||||
IndexQuery query = new IndexQuery();
|
||||
query.setObject(entity);
|
||||
query.setId(stringIdRepresentation(extractIdFromBean(entity)));
|
||||
@@ -274,11 +330,13 @@ public abstract class AbstractElasticsearchRepository<T, ID>
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Class<T> resolveReturnedClassFromGenericType() {
|
||||
|
||||
ParameterizedType parameterizedType = resolveReturnedClassFromGenericType(getClass());
|
||||
return (Class<T>) parameterizedType.getActualTypeArguments()[0];
|
||||
}
|
||||
|
||||
private ParameterizedType resolveReturnedClassFromGenericType(Class<?> clazz) {
|
||||
|
||||
Object genericSuperclass = clazz.getGenericSuperclass();
|
||||
if (genericSuperclass instanceof ParameterizedType) {
|
||||
ParameterizedType parameterizedType = (ParameterizedType) genericSuperclass;
|
||||
@@ -287,11 +345,13 @@ public abstract class AbstractElasticsearchRepository<T, ID>
|
||||
return parameterizedType;
|
||||
}
|
||||
}
|
||||
|
||||
return resolveReturnedClassFromGenericType(clazz.getSuperclass());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<T> getEntityClass() {
|
||||
|
||||
if (!isEntityClassSet()) {
|
||||
try {
|
||||
this.entityClass = resolveReturnedClassFromGenericType();
|
||||
@@ -299,20 +359,25 @@ public abstract class AbstractElasticsearchRepository<T, ID>
|
||||
throw new InvalidDataAccessApiUsageException("Unable to resolve EntityClass. Please use according setter!", e);
|
||||
}
|
||||
}
|
||||
|
||||
return entityClass;
|
||||
}
|
||||
|
||||
private boolean isEntityClassSet() {
|
||||
|
||||
return entityClass != null;
|
||||
}
|
||||
|
||||
public final void setEntityClass(Class<T> entityClass) {
|
||||
|
||||
Assert.notNull(entityClass, "EntityClass must not be null.");
|
||||
this.entityClass = entityClass;
|
||||
}
|
||||
|
||||
public final void setElasticsearchOperations(ElasticsearchOperations elasticsearchOperations) {
|
||||
|
||||
Assert.notNull(elasticsearchOperations, "ElasticsearchOperations must not be null.");
|
||||
|
||||
this.elasticsearchOperations = elasticsearchOperations;
|
||||
}
|
||||
|
||||
@@ -321,11 +386,14 @@ public abstract class AbstractElasticsearchRepository<T, ID>
|
||||
}
|
||||
|
||||
private List<String> stringIdsRepresentation(Iterable<ID> ids) {
|
||||
|
||||
Assert.notNull(ids, "ids can't be null.");
|
||||
|
||||
List<String> stringIds = new ArrayList<>();
|
||||
for (ID id : ids) {
|
||||
stringIds.add(stringIdRepresentation(id));
|
||||
}
|
||||
|
||||
return stringIds;
|
||||
}
|
||||
|
||||
|
||||
+9
-9
@@ -19,7 +19,6 @@ import org.elasticsearch.index.VersionType;
|
||||
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentEntity;
|
||||
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentProperty;
|
||||
import org.springframework.data.repository.core.support.PersistentEntityInformation;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Elasticsearch specific implementation of
|
||||
@@ -34,6 +33,7 @@ import org.springframework.util.Assert;
|
||||
* @author Mark Paluch
|
||||
* @author Christoph Strobl
|
||||
* @author Ivan Greene
|
||||
* @author Sylvain Laurent
|
||||
*/
|
||||
public class MappingElasticsearchEntityInformation<T, ID> extends PersistentEntityInformation<T, ID>
|
||||
implements ElasticsearchEntityInformation<T, ID> {
|
||||
@@ -47,12 +47,10 @@ public class MappingElasticsearchEntityInformation<T, ID> extends PersistentEnti
|
||||
this(entity, entity.getIndexName(), entity.getIndexType(), entity.getVersionType());
|
||||
}
|
||||
|
||||
public MappingElasticsearchEntityInformation(ElasticsearchPersistentEntity<T> entity, String indexName, String type, VersionType versionType) {
|
||||
public MappingElasticsearchEntityInformation(ElasticsearchPersistentEntity<T> entity, String indexName, String type,
|
||||
VersionType versionType) {
|
||||
super(entity);
|
||||
|
||||
Assert.notNull(indexName, "IndexName must not be null!");
|
||||
Assert.notNull(type, "IndexType must not be null!");
|
||||
|
||||
this.entityMetadata = entity;
|
||||
this.indexName = indexName;
|
||||
this.type = type;
|
||||
@@ -66,12 +64,12 @@ public class MappingElasticsearchEntityInformation<T, ID> extends PersistentEnti
|
||||
|
||||
@Override
|
||||
public String getIndexName() {
|
||||
return indexName;
|
||||
return indexName != null ? indexName : entityMetadata.getIndexName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType() {
|
||||
return type;
|
||||
return type != null ? type : entityMetadata.getIndexType();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -79,7 +77,8 @@ public class MappingElasticsearchEntityInformation<T, ID> extends PersistentEnti
|
||||
|
||||
ElasticsearchPersistentProperty versionProperty = entityMetadata.getVersionProperty();
|
||||
try {
|
||||
return versionProperty != null ? (Long) entityMetadata.getPropertyAccessor(entity).getProperty(versionProperty) : null;
|
||||
return versionProperty != null ? (Long) entityMetadata.getPropertyAccessor(entity).getProperty(versionProperty)
|
||||
: null;
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException("failed to load version field", e);
|
||||
}
|
||||
@@ -95,7 +94,8 @@ public class MappingElasticsearchEntityInformation<T, ID> extends PersistentEnti
|
||||
|
||||
ElasticsearchPersistentProperty parentProperty = entityMetadata.getParentIdProperty();
|
||||
try {
|
||||
return parentProperty != null ? (String) entityMetadata.getPropertyAccessor(entity).getProperty(parentProperty) : null;
|
||||
return parentProperty != null ? (String) entityMetadata.getPropertyAccessor(entity).getProperty(parentProperty)
|
||||
: null;
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException("failed to load parent ID: " + e, e);
|
||||
}
|
||||
|
||||
@@ -1,2 +1,6 @@
|
||||
http\://www.springframework.org/schema/data/elasticsearch/spring-elasticsearch-1.0.xsd=org/springframework/data/elasticsearch/config/spring-elasticsearch-1.0.xsd
|
||||
http\://www.springframework.org/schema/data/elasticsearch/spring-elasticsearch-3.2.xsd=org/springframework/data/elasticsearch/config/spring-elasticsearch-3.2.xsd
|
||||
http\://www.springframework.org/schema/data/elasticsearch/spring-elasticsearch.xsd=org/springframework/data/elasticsearch/config/spring-elasticsearch-3.2.xsd
|
||||
https\://www.springframework.org/schema/data/elasticsearch/spring-elasticsearch-1.0.xsd=org/springframework/data/elasticsearch/config/spring-elasticsearch-1.0.xsd
|
||||
https\://www.springframework.org/schema/data/elasticsearch/spring-elasticsearch-3.2.xsd=org/springframework/data/elasticsearch/config/spring-elasticsearch-3.2.xsd
|
||||
https\://www.springframework.org/schema/data/elasticsearch/spring-elasticsearch.xsd=org/springframework/data/elasticsearch/config/spring-elasticsearch-3.2.xsd
|
||||
|
||||
@@ -1,6 +1,135 @@
|
||||
Spring Data Elasticsearch Changelog
|
||||
===================================
|
||||
|
||||
Changes in version 3.2.3.RELEASE (2019-12-04)
|
||||
---------------------------------------------
|
||||
* DATAES-700 - Enable proxy support for RestClient.
|
||||
* DATAES-699 - ElasticsearchRestTemplate.count(..) returns all documents instead of just total hits number.
|
||||
* DATAES-692 - Release 3.2.3 (Moore SR3).
|
||||
|
||||
|
||||
Changes in version 3.1.14.RELEASE (2019-12-04)
|
||||
----------------------------------------------
|
||||
* DATAES-691 - Release 3.1.14 (Lovelace SR14).
|
||||
|
||||
|
||||
Changes in version 3.2.2.RELEASE (2019-11-18)
|
||||
---------------------------------------------
|
||||
* DATAES-685 - Release 3.2.2 (Moore SR2).
|
||||
* DATAES-684 - Implement Bulk Request for Reactive.
|
||||
* DATAES-680 - ReactiveElasticsearchTemplate should use the count API.
|
||||
|
||||
|
||||
Changes in version 3.1.13.RELEASE (2019-11-18)
|
||||
----------------------------------------------
|
||||
* DATAES-683 - Release 3.1.13 (Lovelace SR13).
|
||||
|
||||
|
||||
Changes in version 3.2.1.RELEASE (2019-11-04)
|
||||
---------------------------------------------
|
||||
* DATAES-679 - Upgrade to Elasticsearch 6.8.4.
|
||||
* DATAES-673 - Create a Ssl Rest Client using SslContext and HostnameVerifier.
|
||||
* DATAES-671 - Missing indicesOptions support for scrolling queries.
|
||||
* DATAES-670 - fix version compatibility matrix in documentation.
|
||||
* DATAES-665 - Javadoc not deployed.
|
||||
* DATAES-662 - Release 3.2.1 (Moore SR1).
|
||||
|
||||
|
||||
Changes in version 3.1.12.RELEASE (2019-11-04)
|
||||
----------------------------------------------
|
||||
* DATAES-660 - Release 3.1.12 (Lovelace SR12).
|
||||
|
||||
|
||||
Changes in version 3.2.0.RELEASE (2019-09-30)
|
||||
---------------------------------------------
|
||||
* DATAES-657 - Sort by _score not supported by ElasticsearchRestTemplate.
|
||||
* DATAES-652 - Send if_seq_no and if_primary_term parameters when indexing via ReactiveElasticsearchClient.
|
||||
* DATAES-648 - Unify documentation of version compatibilities.
|
||||
* DATAES-625 - Release 3.2 GA (Moore).
|
||||
* DATAES-541 - IllegalArgumentException on ElasticsearchRestTemplate#removeAlias.
|
||||
* DATAES-305 - let users create mappings with dynamical indexName.
|
||||
* DATAES-227 - ElasticsearchTemplate.prepareUpdate should call setUpsert.
|
||||
|
||||
|
||||
Changes in version 3.1.11.RELEASE (2019-09-30)
|
||||
----------------------------------------------
|
||||
* DATAES-627 - Add HTTPS entries into spring.schemas.
|
||||
* DATAES-624 - Release 3.1.11 (Lovelace SR11).
|
||||
|
||||
|
||||
Changes in version 3.2.0.RC3 (2019-09-06)
|
||||
-----------------------------------------
|
||||
* DATAES-646 - Adapt to Spring Framework 5.2 RC2.
|
||||
* DATAES-645 - Missing highlight support for scrolling queries.
|
||||
* DATAES-643 - RestTemplate ignores search_type.
|
||||
* DATAES-627 - Add HTTPS entries into spring.schemas.
|
||||
* DATAES-626 - Release 3.2 RC3 (Moore).
|
||||
|
||||
|
||||
Changes in version 3.2.0.RC2 (2019-08-05)
|
||||
-----------------------------------------
|
||||
* DATAES-619 - Migrate remaining tests to AssertJ.
|
||||
* DATAES-616 - Fix implementations of ParameterAccessor interface.
|
||||
* DATAES-615 - OrderBy clause on repository queries does not honour @Field name annotation.
|
||||
* DATAES-611 - Adapt to API changes in Spring Webflux.
|
||||
* DATAES-610 - Clarify inclusion of rc and snapshot builds in readme.adoc.
|
||||
* DATAES-609 - Fix TransportClientFactoryBean.isSingleton().
|
||||
* DATAES-607 - Add client security configuration options for Rest clients.
|
||||
* DATAES-606 - update 3.2.x to use ES 6.8.1.
|
||||
* DATAES-604 - Revise readme for a consistent structure.
|
||||
* DATAES-603 - Add support for bulk options in Elasticsearch-Template classes.
|
||||
* DATAES-601 - Fix NoHTTP errors.
|
||||
* DATAES-598 - Add Elasticsearch authentication code example to readme.
|
||||
* DATAES-597 - Fix code formatting in readme.
|
||||
* DATAES-595 - Support for setting preference parameter in query.
|
||||
* DATAES-594 - Support for Index without refresh.
|
||||
* DATAES-593 - Add support for field collapse function.
|
||||
* DATAES-592 - Integrate nohttp tooling into CI build profile.
|
||||
* DATAES-591 - Release 3.2 RC2 (Moore).
|
||||
* DATAES-583 - Introduce Jenkins CI.
|
||||
* DATAES-497 - Revise reference documentation.
|
||||
* DATAES-456 - AbstractElasticsearchRepository.save(S entity) does not evaluate @Document(indexName) expression.
|
||||
* DATAES-415 - More Field Types Support.
|
||||
* DATAES-405 - use @Nullable from org.springframework.lang.
|
||||
* DATAES-387 - Index Not Found Exception When Using Spring EL in Index Name/Type.
|
||||
|
||||
|
||||
Changes in version 3.1.10.RELEASE (2019-08-05)
|
||||
----------------------------------------------
|
||||
* DATAES-592 - Integrate nohttp tooling into CI build profile.
|
||||
* DATAES-590 - Release 3.1.10 (Lovelace SR10).
|
||||
* DATAES-583 - Introduce Jenkins CI.
|
||||
|
||||
|
||||
Changes in version 2.1.23.RELEASE (2019-08-05)
|
||||
----------------------------------------------
|
||||
* DATAES-583 - Introduce Jenkins CI.
|
||||
* DATAES-581 - Release 2.1.23 (Ingalls SR23).
|
||||
|
||||
|
||||
Changes in version 3.2.0.RC1 (2019-06-14)
|
||||
-----------------------------------------
|
||||
* DATAES-589 - improve readme file.
|
||||
* DATAES-586 - Create security policy readme.
|
||||
* DATAES-579 - Test Code Cleanup.
|
||||
* DATAES-560 - Release 3.2 RC1 (Moore).
|
||||
|
||||
|
||||
Changes in version 3.1.9.RELEASE (2019-06-14)
|
||||
---------------------------------------------
|
||||
* DATAES-580 - Release 3.1.9 (Lovelace SR9).
|
||||
|
||||
|
||||
Changes in version 2.1.22.RELEASE (2019-05-13)
|
||||
----------------------------------------------
|
||||
* DATAES-578 - Release 2.1.22 (Ingalls SR22).
|
||||
|
||||
|
||||
Changes in version 3.1.8.RELEASE (2019-05-13)
|
||||
---------------------------------------------
|
||||
* DATAES-577 - Release 3.1.8 (Lovelace SR8).
|
||||
|
||||
|
||||
Changes in version 3.2.0.M4 (2019-05-13)
|
||||
----------------------------------------
|
||||
* DATAES-576 - ElasticsearchTransportTemplateTests.shouldDeleteAcrossIndex(…) consistently fails.
|
||||
@@ -793,3 +922,5 @@ Release Notes - Spring Data Elasticsearch - Version 1.0 M1 (2014-02-07)
|
||||
* #8 - java.lang.NoSuchMethodError: org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentProperty.isVersionProperty()Z
|
||||
* #7 - Missing core types in org.springframework.data.elasticsearch.annotations.FieldType
|
||||
* #6 - spirng-data-elasticsearch with elasticsearch-river-mongodb
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Spring Data Elasticsearch 3.2 M4
|
||||
Spring Data Elasticsearch 3.2.3
|
||||
Copyright (c) [2013-2019] Pivotal Software, Inc.
|
||||
|
||||
This product is licensed to you under the Apache License, Version 2.0 (the "License").
|
||||
@@ -8,3 +8,4 @@ This product may include a number of subcomponents with
|
||||
separate copyright notices and license terms. Your use of the source
|
||||
code for the these subcomponents is subject to the terms and
|
||||
conditions of the subcomponent's license, as noted in the LICENSE file.
|
||||
|
||||
|
||||
@@ -25,38 +25,36 @@ import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A classloader that is a union over the parent core classloader and classloaders of extended plugins.
|
||||
* Cloned from ES repository
|
||||
* - that file is only available in ES server libs
|
||||
* - and we need it o create a node client for unittests
|
||||
* A classloader that is a union over the parent core classloader and classloaders of extended plugins. Cloned from ES
|
||||
* repository - that file is only available in ES server libs - and we need it o create a node client for unittests
|
||||
*/
|
||||
public class ExtendedPluginsClassLoader extends ClassLoader {
|
||||
|
||||
/** Loaders of plugins extended by a plugin. */
|
||||
private final List<ClassLoader> extendedLoaders;
|
||||
/** Loaders of plugins extended by a plugin. */
|
||||
private final List<ClassLoader> extendedLoaders;
|
||||
|
||||
private ExtendedPluginsClassLoader(ClassLoader parent, List<ClassLoader> extendedLoaders) {
|
||||
super(parent);
|
||||
this.extendedLoaders = Collections.unmodifiableList(extendedLoaders);
|
||||
}
|
||||
private ExtendedPluginsClassLoader(ClassLoader parent, List<ClassLoader> extendedLoaders) {
|
||||
super(parent);
|
||||
this.extendedLoaders = Collections.unmodifiableList(extendedLoaders);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?> findClass(String name) throws ClassNotFoundException {
|
||||
for (ClassLoader loader : extendedLoaders) {
|
||||
try {
|
||||
return loader.loadClass(name);
|
||||
} catch (ClassNotFoundException e) {
|
||||
// continue
|
||||
}
|
||||
}
|
||||
throw new ClassNotFoundException(name);
|
||||
}
|
||||
@Override
|
||||
protected Class<?> findClass(String name) throws ClassNotFoundException {
|
||||
for (ClassLoader loader : extendedLoaders) {
|
||||
try {
|
||||
return loader.loadClass(name);
|
||||
} catch (ClassNotFoundException e) {
|
||||
// continue
|
||||
}
|
||||
}
|
||||
throw new ClassNotFoundException(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new classloader across the parent and extended loaders.
|
||||
*/
|
||||
public static ExtendedPluginsClassLoader create(ClassLoader parent, List<ClassLoader> extendedLoaders) {
|
||||
return AccessController.doPrivileged((PrivilegedAction<ExtendedPluginsClassLoader>)
|
||||
() -> new ExtendedPluginsClassLoader(parent, extendedLoaders));
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Return a new classloader across the parent and extended loaders.
|
||||
*/
|
||||
public static ExtendedPluginsClassLoader create(ClassLoader parent, List<ClassLoader> extendedLoaders) {
|
||||
return AccessController.doPrivileged(
|
||||
(PrivilegedAction<ExtendedPluginsClassLoader>) () -> new ExtendedPluginsClassLoader(parent, extendedLoaders));
|
||||
}
|
||||
}
|
||||
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2019 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.elasticsearch.client.Client;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
import org.springframework.data.elasticsearch.config.ElasticsearchConfigurationSupport;
|
||||
import org.springframework.data.elasticsearch.core.ElasticsearchEntityMapper;
|
||||
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
|
||||
import org.springframework.data.elasticsearch.core.EntityMapper;
|
||||
|
||||
/**
|
||||
* configuration class for the classic ElasticsearchTemplate. Needs a {@link TestNodeResource} bean that should be set up in
|
||||
* the test as ClassRule and exported as bean.
|
||||
*
|
||||
* @author Peter-Josef Meisch
|
||||
*/
|
||||
@Configuration
|
||||
public class ElasticsearchTestConfiguration extends ElasticsearchConfigurationSupport {
|
||||
|
||||
@Autowired private TestNodeResource testNodeResource;
|
||||
|
||||
@Bean
|
||||
public Client elasticsearchClient() {
|
||||
return testNodeResource.client();
|
||||
}
|
||||
|
||||
@Bean(name = { "elasticsearchOperations", "elasticsearchTemplate" })
|
||||
public ElasticsearchTemplate elasticsearchTemplate(Client elasticsearchClient, EntityMapper entityMapper) {
|
||||
return new ElasticsearchTemplate(elasticsearchClient, entityMapper);
|
||||
}
|
||||
|
||||
/*
|
||||
* need the ElasticsearchMapper, because some tests rely on @Field(name) being handled correctly
|
||||
*/
|
||||
@Bean
|
||||
@Override
|
||||
public EntityMapper entityMapper() {
|
||||
ElasticsearchEntityMapper entityMapper = new ElasticsearchEntityMapper(elasticsearchMappingContext(),
|
||||
new DefaultConversionService());
|
||||
entityMapper.setConversions(elasticsearchCustomConversions());
|
||||
|
||||
return entityMapper;
|
||||
}
|
||||
}
|
||||
@@ -59,7 +59,7 @@ public class ElasticsearchVersionRule implements TestRule {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Statement apply(final Statement base, Description description) {
|
||||
public Statement apply(Statement base, Description description) {
|
||||
|
||||
return new Statement() {
|
||||
|
||||
|
||||
@@ -1,73 +0,0 @@
|
||||
/*
|
||||
* Copyright 2014-2019 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 static org.apache.commons.lang.RandomStringUtils.*;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
|
||||
import org.springframework.data.elasticsearch.entities.Author;
|
||||
import org.springframework.data.elasticsearch.entities.Book;
|
||||
import org.springframework.data.elasticsearch.repositories.book.SampleElasticSearchBookRepository;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
/**
|
||||
* @author Mohsin Husen
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration("classpath:/repository-test-nested-object-books.xml")
|
||||
public class InnerObjectTests {
|
||||
|
||||
@Autowired private SampleElasticSearchBookRepository bookRepository;
|
||||
|
||||
@Autowired private ElasticsearchTemplate elasticsearchTemplate;
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
elasticsearchTemplate.deleteIndex(Book.class);
|
||||
elasticsearchTemplate.createIndex(Book.class);
|
||||
elasticsearchTemplate.putMapping(Book.class);
|
||||
elasticsearchTemplate.refresh(Book.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldIndexInnerObject() {
|
||||
// given
|
||||
String id = randomAlphanumeric(5);
|
||||
Book book = new Book();
|
||||
book.setId(id);
|
||||
book.setName("xyz");
|
||||
Author author = new Author();
|
||||
author.setId("1");
|
||||
author.setName("ABC");
|
||||
book.setAuthor(author);
|
||||
// when
|
||||
bookRepository.save(book);
|
||||
// then
|
||||
assertThat(bookRepository.findById(id), is(notNullValue()));
|
||||
}
|
||||
}
|
||||
@@ -16,14 +16,17 @@
|
||||
package org.springframework.data.elasticsearch;
|
||||
|
||||
import static org.apache.commons.lang.RandomStringUtils.*;
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.elasticsearch.index.query.QueryBuilders.*;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -35,64 +38,57 @@ import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.elasticsearch.annotations.Document;
|
||||
import org.springframework.data.elasticsearch.annotations.Field;
|
||||
import org.springframework.data.elasticsearch.annotations.FieldType;
|
||||
import org.springframework.data.elasticsearch.annotations.InnerField;
|
||||
import org.springframework.data.elasticsearch.annotations.MultiField;
|
||||
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
|
||||
import org.springframework.data.elasticsearch.core.query.GetQuery;
|
||||
import org.springframework.data.elasticsearch.core.query.IndexQuery;
|
||||
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
|
||||
import org.springframework.data.elasticsearch.core.query.SearchQuery;
|
||||
import org.springframework.data.elasticsearch.entities.Author;
|
||||
import org.springframework.data.elasticsearch.entities.Book;
|
||||
import org.springframework.data.elasticsearch.entities.Car;
|
||||
import org.springframework.data.elasticsearch.entities.GirlFriend;
|
||||
import org.springframework.data.elasticsearch.entities.Person;
|
||||
import org.springframework.data.elasticsearch.entities.PersonMultipleLevelNested;
|
||||
import org.springframework.data.elasticsearch.utils.IndexInitializer;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
/**
|
||||
* @author Rizwan Idrees
|
||||
* @author Mohsin Husen
|
||||
* @author Artur Konczak
|
||||
* @author Peter-Josef Meisch
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration("classpath:/repository-test-nested-object.xml")
|
||||
@RunWith(SpringRunner.class)
|
||||
@ContextConfiguration("classpath:/elasticsearch-template-test.xml")
|
||||
public class NestedObjectTests {
|
||||
|
||||
@Autowired
|
||||
private ElasticsearchTemplate elasticsearchTemplate;
|
||||
|
||||
@Autowired private ElasticsearchTemplate elasticsearchTemplate;
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
elasticsearchTemplate.deleteIndex(Book.class);
|
||||
elasticsearchTemplate.createIndex(Book.class);
|
||||
elasticsearchTemplate.putMapping(Book.class);
|
||||
elasticsearchTemplate.refresh(Book.class);
|
||||
elasticsearchTemplate.deleteIndex(Person.class);
|
||||
elasticsearchTemplate.createIndex(Person.class);
|
||||
elasticsearchTemplate.putMapping(Person.class);
|
||||
elasticsearchTemplate.refresh(Person.class);
|
||||
elasticsearchTemplate.deleteIndex(PersonMultipleLevelNested.class);
|
||||
elasticsearchTemplate.createIndex(PersonMultipleLevelNested.class);
|
||||
elasticsearchTemplate.putMapping(PersonMultipleLevelNested.class);
|
||||
elasticsearchTemplate.refresh(PersonMultipleLevelNested.class);
|
||||
|
||||
IndexInitializer.init(elasticsearchTemplate, Book.class);
|
||||
IndexInitializer.init(elasticsearchTemplate, Person.class);
|
||||
IndexInitializer.init(elasticsearchTemplate, PersonMultipleLevelNested.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldIndexInitialLevelNestedObject() {
|
||||
|
||||
final List<Car> cars = new ArrayList<>();
|
||||
List<Car> cars = new ArrayList<>();
|
||||
|
||||
final Car saturn = new Car();
|
||||
Car saturn = new Car();
|
||||
saturn.setName("Saturn");
|
||||
saturn.setModel("SL");
|
||||
|
||||
final Car subaru = new Car();
|
||||
Car subaru = new Car();
|
||||
subaru.setName("Subaru");
|
||||
subaru.setModel("Imprezza");
|
||||
|
||||
final Car ford = new Car();
|
||||
Car ford = new Car();
|
||||
ford.setName("Ford");
|
||||
ford.setModel("Focus");
|
||||
|
||||
@@ -100,158 +96,156 @@ public class NestedObjectTests {
|
||||
cars.add(subaru);
|
||||
cars.add(ford);
|
||||
|
||||
final Person foo = new Person();
|
||||
Person foo = new Person();
|
||||
foo.setName("Foo");
|
||||
foo.setId("1");
|
||||
foo.setCar(cars);
|
||||
|
||||
final Car car = new Car();
|
||||
Car car = new Car();
|
||||
car.setName("Saturn");
|
||||
car.setModel("Imprezza");
|
||||
|
||||
final Person bar = new Person();
|
||||
Person bar = new Person();
|
||||
bar.setId("2");
|
||||
bar.setName("Bar");
|
||||
bar.setCar(Arrays.asList(car));
|
||||
bar.setCar(Collections.singletonList(car));
|
||||
|
||||
final List<IndexQuery> indexQueries = new ArrayList<>();
|
||||
final IndexQuery indexQuery1 = new IndexQuery();
|
||||
List<IndexQuery> indexQueries = new ArrayList<>();
|
||||
IndexQuery indexQuery1 = new IndexQuery();
|
||||
indexQuery1.setId(foo.getId());
|
||||
indexQuery1.setObject(foo);
|
||||
|
||||
final IndexQuery indexQuery2 = new IndexQuery();
|
||||
IndexQuery indexQuery2 = new IndexQuery();
|
||||
indexQuery2.setId(bar.getId());
|
||||
indexQuery2.setObject(bar);
|
||||
|
||||
indexQueries.add(indexQuery1);
|
||||
indexQueries.add(indexQuery2);
|
||||
|
||||
elasticsearchTemplate.putMapping(Person.class);
|
||||
elasticsearchTemplate.bulkIndex(indexQueries);
|
||||
elasticsearchTemplate.refresh(Person.class);
|
||||
|
||||
final QueryBuilder builder = nestedQuery("car", boolQuery().must(termQuery("car.name", "saturn")).must(termQuery("car.model", "imprezza")), ScoreMode.None);
|
||||
QueryBuilder builder = nestedQuery("car",
|
||||
boolQuery().must(termQuery("car.name", "saturn")).must(termQuery("car.model", "imprezza")), ScoreMode.None);
|
||||
|
||||
final SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(builder).build();
|
||||
final List<Person> persons = elasticsearchTemplate.queryForList(searchQuery, Person.class);
|
||||
SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(builder).build();
|
||||
List<Person> persons = elasticsearchTemplate.queryForList(searchQuery, Person.class);
|
||||
|
||||
assertThat(persons.size(), is(1));
|
||||
assertThat(persons).hasSize(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldIndexMultipleLevelNestedObject() {
|
||||
//given
|
||||
final List<IndexQuery> indexQueries = createPerson();
|
||||
|
||||
//when
|
||||
elasticsearchTemplate.putMapping(PersonMultipleLevelNested.class);
|
||||
// given
|
||||
List<IndexQuery> indexQueries = createPerson();
|
||||
|
||||
// when
|
||||
elasticsearchTemplate.bulkIndex(indexQueries);
|
||||
elasticsearchTemplate.refresh(PersonMultipleLevelNested.class);
|
||||
|
||||
//then
|
||||
final GetQuery getQuery = new GetQuery();
|
||||
// then
|
||||
GetQuery getQuery = new GetQuery();
|
||||
getQuery.setId("1");
|
||||
final PersonMultipleLevelNested personIndexed = elasticsearchTemplate.queryForObject(getQuery, PersonMultipleLevelNested.class);
|
||||
assertThat(personIndexed, is(notNullValue()));
|
||||
PersonMultipleLevelNested personIndexed = elasticsearchTemplate.queryForObject(getQuery,
|
||||
PersonMultipleLevelNested.class);
|
||||
assertThat(personIndexed).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldIndexMultipleLevelNestedObjectWithIncludeInParent() {
|
||||
//given
|
||||
final List<IndexQuery> indexQueries = createPerson();
|
||||
|
||||
//when
|
||||
elasticsearchTemplate.putMapping(PersonMultipleLevelNested.class);
|
||||
// given
|
||||
List<IndexQuery> indexQueries = createPerson();
|
||||
|
||||
// when
|
||||
elasticsearchTemplate.bulkIndex(indexQueries);
|
||||
|
||||
// then
|
||||
Map<String, Object> mapping = elasticsearchTemplate.getMapping(PersonMultipleLevelNested.class);
|
||||
|
||||
final Map mapping = elasticsearchTemplate.getMapping(PersonMultipleLevelNested.class);
|
||||
|
||||
assertThat(mapping, is(notNullValue()));
|
||||
final Map propertyMap = (Map) mapping.get("properties");
|
||||
assertThat(propertyMap, is(notNullValue()));
|
||||
final Map bestCarsAttributes = (Map) propertyMap.get("bestCars");
|
||||
assertThat(bestCarsAttributes.get("include_in_parent"), is(notNullValue()));
|
||||
assertThat(mapping).isNotNull();
|
||||
Map<String, Object> propertyMap = (Map<String, Object>) mapping.get("properties");
|
||||
assertThat(propertyMap).isNotNull();
|
||||
Map bestCarsAttributes = (Map) propertyMap.get("bestCars");
|
||||
assertThat(bestCarsAttributes.get("include_in_parent")).isNotNull();
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void shouldSearchUsingNestedQueryOnMultipleLevelNestedObject() {
|
||||
//given
|
||||
final List<IndexQuery> indexQueries = createPerson();
|
||||
|
||||
//when
|
||||
elasticsearchTemplate.putMapping(PersonMultipleLevelNested.class);
|
||||
// given
|
||||
List<IndexQuery> indexQueries = createPerson();
|
||||
|
||||
// when
|
||||
elasticsearchTemplate.bulkIndex(indexQueries);
|
||||
elasticsearchTemplate.refresh(PersonMultipleLevelNested.class);
|
||||
|
||||
//then
|
||||
final BoolQueryBuilder builder = boolQuery();
|
||||
builder.must(nestedQuery("girlFriends", termQuery("girlFriends.type", "temp"),ScoreMode.None))
|
||||
.must(nestedQuery("girlFriends.cars", termQuery("girlFriends.cars.name", "Ford".toLowerCase()),ScoreMode.None));
|
||||
// then
|
||||
BoolQueryBuilder builder = boolQuery();
|
||||
builder.must(nestedQuery("girlFriends", termQuery("girlFriends.type", "temp"), ScoreMode.None)).must(
|
||||
nestedQuery("girlFriends.cars", termQuery("girlFriends.cars.name", "Ford".toLowerCase()), ScoreMode.None));
|
||||
|
||||
final SearchQuery searchQuery = new NativeSearchQueryBuilder()
|
||||
.withQuery(builder)
|
||||
.build();
|
||||
SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(builder).build();
|
||||
|
||||
final Page<PersonMultipleLevelNested> personIndexed = elasticsearchTemplate.queryForPage(searchQuery, PersonMultipleLevelNested.class);
|
||||
assertThat(personIndexed, is(notNullValue()));
|
||||
assertThat(personIndexed.getTotalElements(), is(1L));
|
||||
assertThat(personIndexed.getContent().get(0).getId(), is("1"));
|
||||
Page<PersonMultipleLevelNested> personIndexed = elasticsearchTemplate.queryForPage(searchQuery,
|
||||
PersonMultipleLevelNested.class);
|
||||
assertThat(personIndexed).isNotNull();
|
||||
assertThat(personIndexed.getTotalElements()).isEqualTo(1);
|
||||
assertThat(personIndexed.getContent().get(0).getId()).isEqualTo("1");
|
||||
}
|
||||
|
||||
|
||||
private List<IndexQuery> createPerson() {
|
||||
|
||||
final PersonMultipleLevelNested person1 = new PersonMultipleLevelNested();
|
||||
PersonMultipleLevelNested person1 = new PersonMultipleLevelNested();
|
||||
|
||||
person1.setId("1");
|
||||
person1.setName("name");
|
||||
|
||||
final Car saturn = new Car();
|
||||
Car saturn = new Car();
|
||||
saturn.setName("Saturn");
|
||||
saturn.setModel("SL");
|
||||
|
||||
final Car subaru = new Car();
|
||||
Car subaru = new Car();
|
||||
subaru.setName("Subaru");
|
||||
subaru.setModel("Imprezza");
|
||||
|
||||
final Car car = new Car();
|
||||
Car car = new Car();
|
||||
car.setName("Saturn");
|
||||
car.setModel("Imprezza");
|
||||
|
||||
final Car ford = new Car();
|
||||
Car ford = new Car();
|
||||
ford.setName("Ford");
|
||||
ford.setModel("Focus");
|
||||
|
||||
final GirlFriend permanent = new GirlFriend();
|
||||
GirlFriend permanent = new GirlFriend();
|
||||
permanent.setName("permanent");
|
||||
permanent.setType("permanent");
|
||||
permanent.setCars(Arrays.asList(saturn, subaru));
|
||||
|
||||
final GirlFriend temp = new GirlFriend();
|
||||
GirlFriend temp = new GirlFriend();
|
||||
temp.setName("temp");
|
||||
temp.setType("temp");
|
||||
temp.setCars(Arrays.asList(car, ford));
|
||||
|
||||
person1.setGirlFriends(Arrays.asList(permanent, temp));
|
||||
|
||||
final IndexQuery indexQuery1 = new IndexQuery();
|
||||
IndexQuery indexQuery1 = new IndexQuery();
|
||||
indexQuery1.setId(person1.getId());
|
||||
indexQuery1.setObject(person1);
|
||||
|
||||
final PersonMultipleLevelNested person2 = new PersonMultipleLevelNested();
|
||||
PersonMultipleLevelNested person2 = new PersonMultipleLevelNested();
|
||||
|
||||
person2.setId("2");
|
||||
person2.setName("name");
|
||||
|
||||
person2.setGirlFriends(Arrays.asList(permanent));
|
||||
person2.setGirlFriends(Collections.singletonList(permanent));
|
||||
|
||||
final IndexQuery indexQuery2 = new IndexQuery();
|
||||
IndexQuery indexQuery2 = new IndexQuery();
|
||||
indexQuery2.setId(person2.getId());
|
||||
indexQuery2.setObject(person2);
|
||||
|
||||
final List<IndexQuery> indexQueries = new ArrayList<>();
|
||||
List<IndexQuery> indexQueries = new ArrayList<>();
|
||||
indexQueries.add(indexQuery1);
|
||||
indexQueries.add(indexQuery2);
|
||||
|
||||
@@ -261,17 +255,18 @@ public class NestedObjectTests {
|
||||
@Test
|
||||
public void shouldSearchBooksForPersonInitialLevelNestedType() {
|
||||
|
||||
final List<Car> cars = new ArrayList<>();
|
||||
// given
|
||||
List<Car> cars = new ArrayList<>();
|
||||
|
||||
final Car saturn = new Car();
|
||||
Car saturn = new Car();
|
||||
saturn.setName("Saturn");
|
||||
saturn.setModel("SL");
|
||||
|
||||
final Car subaru = new Car();
|
||||
Car subaru = new Car();
|
||||
subaru.setName("Subaru");
|
||||
subaru.setModel("Imprezza");
|
||||
|
||||
final Car ford = new Car();
|
||||
Car ford = new Car();
|
||||
ford.setName("Ford");
|
||||
ford.setModel("Focus");
|
||||
|
||||
@@ -279,69 +274,68 @@ public class NestedObjectTests {
|
||||
cars.add(subaru);
|
||||
cars.add(ford);
|
||||
|
||||
final Book java = new Book();
|
||||
Book java = new Book();
|
||||
java.setId("1");
|
||||
java.setName("java");
|
||||
final Author javaAuthor = new Author();
|
||||
Author javaAuthor = new Author();
|
||||
javaAuthor.setId("1");
|
||||
javaAuthor.setName("javaAuthor");
|
||||
java.setAuthor(javaAuthor);
|
||||
|
||||
final Book spring = new Book();
|
||||
Book spring = new Book();
|
||||
spring.setId("2");
|
||||
spring.setName("spring");
|
||||
final Author springAuthor = new Author();
|
||||
Author springAuthor = new Author();
|
||||
springAuthor.setId("2");
|
||||
springAuthor.setName("springAuthor");
|
||||
spring.setAuthor(springAuthor);
|
||||
|
||||
final Person foo = new Person();
|
||||
Person foo = new Person();
|
||||
foo.setName("Foo");
|
||||
foo.setId("1");
|
||||
foo.setCar(cars);
|
||||
foo.setBooks(Arrays.asList(java, spring));
|
||||
|
||||
final Car car = new Car();
|
||||
Car car = new Car();
|
||||
car.setName("Saturn");
|
||||
car.setModel("Imprezza");
|
||||
|
||||
final Person bar = new Person();
|
||||
Person bar = new Person();
|
||||
bar.setId("2");
|
||||
bar.setName("Bar");
|
||||
bar.setCar(Arrays.asList(car));
|
||||
bar.setCar(Collections.singletonList(car));
|
||||
|
||||
final List<IndexQuery> indexQueries = new ArrayList<>();
|
||||
final IndexQuery indexQuery1 = new IndexQuery();
|
||||
List<IndexQuery> indexQueries = new ArrayList<>();
|
||||
IndexQuery indexQuery1 = new IndexQuery();
|
||||
indexQuery1.setId(foo.getId());
|
||||
indexQuery1.setObject(foo);
|
||||
|
||||
final IndexQuery indexQuery2 = new IndexQuery();
|
||||
IndexQuery indexQuery2 = new IndexQuery();
|
||||
indexQuery2.setId(bar.getId());
|
||||
indexQuery2.setObject(bar);
|
||||
|
||||
indexQueries.add(indexQuery1);
|
||||
indexQueries.add(indexQuery2);
|
||||
|
||||
elasticsearchTemplate.putMapping(Person.class);
|
||||
elasticsearchTemplate.bulkIndex(indexQueries);
|
||||
elasticsearchTemplate.refresh(Person.class);
|
||||
|
||||
final QueryBuilder builder = nestedQuery("books", boolQuery().must(termQuery("books.name", "java")), ScoreMode.None);
|
||||
// when
|
||||
QueryBuilder builder = nestedQuery("books", boolQuery().must(termQuery("books.name", "java")), ScoreMode.None);
|
||||
|
||||
final SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(builder).build();
|
||||
final List<Person> persons = elasticsearchTemplate.queryForList(searchQuery, Person.class);
|
||||
SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(builder).build();
|
||||
List<Person> persons = elasticsearchTemplate.queryForList(searchQuery, Person.class);
|
||||
|
||||
assertThat(persons.size(), is(1));
|
||||
// then
|
||||
assertThat(persons).hasSize(1);
|
||||
}
|
||||
|
||||
/*
|
||||
DATAES-73
|
||||
*/
|
||||
@Test
|
||||
@Test // DATAES-73
|
||||
public void shouldIndexAndSearchMapAsNestedType() {
|
||||
//given
|
||||
final Book book1 = new Book();
|
||||
final Book book2 = new Book();
|
||||
|
||||
// given
|
||||
Book book1 = new Book();
|
||||
Book book2 = new Book();
|
||||
|
||||
book1.setId(randomNumeric(5));
|
||||
book1.setName("testBook1");
|
||||
@@ -349,37 +343,118 @@ public class NestedObjectTests {
|
||||
book2.setId(randomNumeric(5));
|
||||
book2.setName("testBook2");
|
||||
|
||||
final Map<Integer, Collection<String>> map1 = new HashMap<>();
|
||||
Map<Integer, Collection<String>> map1 = new HashMap<>();
|
||||
map1.put(1, Arrays.asList("test1", "test2"));
|
||||
|
||||
final Map<Integer, Collection<String>> map2 = new HashMap<>();
|
||||
Map<Integer, Collection<String>> map2 = new HashMap<>();
|
||||
map2.put(1, Arrays.asList("test3", "test4"));
|
||||
|
||||
book1.setBuckets(map1);
|
||||
book2.setBuckets(map2);
|
||||
|
||||
final List<IndexQuery> indexQueries = new ArrayList<>();
|
||||
final IndexQuery indexQuery1 = new IndexQuery();
|
||||
List<IndexQuery> indexQueries = new ArrayList<>();
|
||||
IndexQuery indexQuery1 = new IndexQuery();
|
||||
indexQuery1.setId(book1.getId());
|
||||
indexQuery1.setObject(book1);
|
||||
|
||||
final IndexQuery indexQuery2 = new IndexQuery();
|
||||
IndexQuery indexQuery2 = new IndexQuery();
|
||||
indexQuery2.setId(book2.getId());
|
||||
indexQuery2.setObject(book2);
|
||||
|
||||
indexQueries.add(indexQuery1);
|
||||
indexQueries.add(indexQuery2);
|
||||
//when
|
||||
|
||||
// when
|
||||
elasticsearchTemplate.bulkIndex(indexQueries);
|
||||
elasticsearchTemplate.refresh(Book.class);
|
||||
//then
|
||||
final SearchQuery searchQuery = new NativeSearchQueryBuilder()
|
||||
.withQuery(nestedQuery("buckets", termQuery("buckets.1", "test3"),ScoreMode.None))
|
||||
.build();
|
||||
final Page<Book> books = elasticsearchTemplate.queryForPage(searchQuery, Book.class);
|
||||
|
||||
assertThat(books.getContent().size(), is(1));
|
||||
assertThat(books.getContent().get(0).getId(), is(book2.getId()));
|
||||
// then
|
||||
SearchQuery searchQuery = new NativeSearchQueryBuilder()
|
||||
.withQuery(nestedQuery("buckets", termQuery("buckets.1", "test3"), ScoreMode.None)).build();
|
||||
Page<Book> books = elasticsearchTemplate.queryForPage(searchQuery, Book.class);
|
||||
|
||||
assertThat(books.getContent()).hasSize(1);
|
||||
assertThat(books.getContent().get(0).getId()).isEqualTo(book2.getId());
|
||||
}
|
||||
}
|
||||
|
||||
@Setter
|
||||
@Getter
|
||||
@Document(indexName = "test-index-book-nested-objects", type = "book", shards = 1, replicas = 0,
|
||||
refreshInterval = "-1")
|
||||
static class Book {
|
||||
|
||||
@Id private String id;
|
||||
private String name;
|
||||
@Field(type = FieldType.Object) private Author author;
|
||||
@Field(type = FieldType.Nested) private Map<Integer, Collection<String>> buckets = new HashMap<>();
|
||||
@MultiField(mainField = @Field(type = FieldType.Text, analyzer = "whitespace"),
|
||||
otherFields = { @InnerField(suffix = "prefix", type = FieldType.Text, analyzer = "stop",
|
||||
searchAnalyzer = "standard") }) private String description;
|
||||
}
|
||||
|
||||
@Data
|
||||
@Document(indexName = "test-index-person", type = "user", shards = 1, replicas = 0, refreshInterval = "-1")
|
||||
static class Person {
|
||||
|
||||
@Id private String id;
|
||||
|
||||
private String name;
|
||||
|
||||
@Field(type = FieldType.Nested) private List<Car> car;
|
||||
|
||||
@Field(type = FieldType.Nested, includeInParent = true) private List<Book> books;
|
||||
}
|
||||
|
||||
@Data
|
||||
static class Car {
|
||||
|
||||
private String name;
|
||||
private String model;
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Rizwan Idrees
|
||||
* @author Mohsin Husen
|
||||
* @author Artur Konczak
|
||||
*/
|
||||
@Data
|
||||
@Document(indexName = "test-index-person-multiple-level-nested", type = "user", shards = 1, replicas = 0,
|
||||
refreshInterval = "-1")
|
||||
static class PersonMultipleLevelNested {
|
||||
|
||||
@Id private String id;
|
||||
|
||||
private String name;
|
||||
|
||||
@Field(type = FieldType.Nested) private List<GirlFriend> girlFriends;
|
||||
|
||||
@Field(type = FieldType.Nested) private List<Car> cars;
|
||||
|
||||
@Field(type = FieldType.Nested, includeInParent = true) private List<Car> bestCars;
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Mohsin Husen
|
||||
*/
|
||||
@Data
|
||||
static class GirlFriend {
|
||||
|
||||
private String name;
|
||||
|
||||
private String type;
|
||||
|
||||
@Field(type = FieldType.Nested) private List<Car> cars;
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Rizwan Idrees
|
||||
* @author Mohsin Husen
|
||||
*/
|
||||
@Data
|
||||
static class Author {
|
||||
|
||||
private String id;
|
||||
private String name;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2019 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.elasticsearch.client.RestHighLevelClient;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
import org.springframework.data.elasticsearch.config.AbstractElasticsearchConfiguration;
|
||||
import org.springframework.data.elasticsearch.core.ElasticsearchEntityMapper;
|
||||
import org.springframework.data.elasticsearch.core.EntityMapper;
|
||||
|
||||
/**
|
||||
* @author Peter-Josef Meisch
|
||||
*/
|
||||
@Configuration
|
||||
public class RestElasticsearchTestConfiguration extends AbstractElasticsearchConfiguration {
|
||||
|
||||
@Override
|
||||
@Bean
|
||||
public RestHighLevelClient elasticsearchClient() {
|
||||
return TestUtils.restHighLevelClient();
|
||||
}
|
||||
|
||||
/*
|
||||
* need the ElasticsearchMapper, because some tests rely on @Field(name) being handled correctly
|
||||
*/
|
||||
@Bean
|
||||
@Override
|
||||
public EntityMapper entityMapper() {
|
||||
ElasticsearchEntityMapper entityMapper = new ElasticsearchEntityMapper(elasticsearchMappingContext(),
|
||||
new DefaultConversionService());
|
||||
entityMapper.setConversions(elasticsearchCustomConversions());
|
||||
|
||||
return entityMapper;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2019 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.io.IOException;
|
||||
|
||||
import org.elasticsearch.client.Client;
|
||||
import org.elasticsearch.node.Node;
|
||||
import org.junit.rules.ExternalResource;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* JUnit4 Rule that sets up and tears down a local Elasticsearch node.
|
||||
*
|
||||
* @author Peter-Josef Meisch
|
||||
*/
|
||||
public class TestNodeResource extends ExternalResource {
|
||||
|
||||
private static Node node;
|
||||
|
||||
@Override
|
||||
protected void before() throws Throwable {
|
||||
node = Utils.getNode();
|
||||
node.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void after() {
|
||||
if (node != null) {
|
||||
try {
|
||||
node.close();
|
||||
} catch (IOException ignored) {}
|
||||
}
|
||||
}
|
||||
|
||||
public Client client() {
|
||||
Assert.notNull(node, "node is not initialized");
|
||||
return node.client();
|
||||
}
|
||||
}
|
||||
@@ -15,35 +15,42 @@
|
||||
*/
|
||||
package org.springframework.data.elasticsearch;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.elasticsearch.client.Client;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.node.Node;
|
||||
import org.elasticsearch.node.NodeValidationException;
|
||||
import org.elasticsearch.transport.Netty4Plugin;
|
||||
import org.springframework.data.elasticsearch.client.NodeClientFactoryBean;
|
||||
import static java.util.Arrays.*;
|
||||
|
||||
/**
|
||||
* @author Mohsin Husen
|
||||
* @author Artur Konczak
|
||||
* @author Ilkang Na
|
||||
* @author Peter-Josef Meisch
|
||||
*/
|
||||
public class Utils {
|
||||
|
||||
public static Client getNodeClient() throws NodeValidationException {
|
||||
|
||||
public static Node getNode() {
|
||||
String pathHome = "src/test/resources/test-home-dir";
|
||||
String pathData = "target/elasticsearchTestData";
|
||||
String clusterName = UUID.randomUUID().toString();
|
||||
|
||||
return new NodeClientFactoryBean.TestNode(
|
||||
Settings.builder()
|
||||
.put("transport.type", "netty4")
|
||||
.put("http.type", "netty4")
|
||||
.put("path.home", pathHome)
|
||||
.put("path.data", pathData)
|
||||
.put("cluster.name", clusterName)
|
||||
.put("node.max_local_storage_nodes", 100)
|
||||
.build(), asList(Netty4Plugin.class)).start().client();
|
||||
return new NodeClientFactoryBean.TestNode( //
|
||||
Settings.builder() //
|
||||
.put("transport.type", "netty4") //
|
||||
.put("http.type", "netty4") //
|
||||
.put("path.home", pathHome) //
|
||||
.put("path.data", pathData) //
|
||||
.put("cluster.name", clusterName) //
|
||||
.put("node.max_local_storage_nodes", 100)//
|
||||
.build(), //
|
||||
Collections.singletonList(Netty4Plugin.class));
|
||||
}
|
||||
|
||||
public static Client getNodeClient() throws NodeValidationException {
|
||||
return getNode().start().client();
|
||||
}
|
||||
}
|
||||
|
||||
-55
@@ -1,55 +0,0 @@
|
||||
/*
|
||||
* Copyright 2014-2019 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.builder;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import org.springframework.data.elasticsearch.core.query.IndexQuery;
|
||||
import org.springframework.data.elasticsearch.entities.SampleInheritedEntity;
|
||||
|
||||
/**
|
||||
* @author Kevin Leturc
|
||||
*/
|
||||
public class SampleInheritedEntityBuilder {
|
||||
|
||||
private SampleInheritedEntity result;
|
||||
|
||||
public SampleInheritedEntityBuilder(String id) {
|
||||
result = new SampleInheritedEntity();
|
||||
result.setId(id);
|
||||
}
|
||||
|
||||
public SampleInheritedEntityBuilder createdDate(Date createdDate) {
|
||||
result.setCreatedDate(createdDate);
|
||||
return this;
|
||||
}
|
||||
|
||||
public SampleInheritedEntityBuilder message(String message) {
|
||||
result.setMessage(message);
|
||||
return this;
|
||||
}
|
||||
|
||||
public SampleInheritedEntity build() {
|
||||
return result;
|
||||
}
|
||||
|
||||
public IndexQuery buildIndex() {
|
||||
IndexQuery indexQuery = new IndexQuery();
|
||||
indexQuery.setId(result.getId());
|
||||
indexQuery.setObject(result);
|
||||
return indexQuery;
|
||||
}
|
||||
}
|
||||
+70
-2
@@ -23,6 +23,7 @@ import java.time.Duration;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
|
||||
import org.apache.http.conn.ssl.NoopHostnameVerifier;
|
||||
import org.junit.Test;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
|
||||
@@ -30,6 +31,8 @@ import org.springframework.http.HttpHeaders;
|
||||
* Unit tests for {@link ClientConfiguration}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @author Peter-Josef Meisch
|
||||
* @author Henrique Amaral
|
||||
*/
|
||||
public class ClientConfigurationUnitTests {
|
||||
|
||||
@@ -41,7 +44,7 @@ public class ClientConfigurationUnitTests {
|
||||
assertThat(clientConfiguration.getEndpoints()).containsOnly(InetSocketAddress.createUnresolved("localhost", 9200));
|
||||
}
|
||||
|
||||
@Test // DATAES-488, DATAES-504
|
||||
@Test // DATAES-488, DATAES-504, DATAES-650, DATAES-700
|
||||
public void shouldCreateCustomizedConfiguration() {
|
||||
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
@@ -51,7 +54,8 @@ public class ClientConfigurationUnitTests {
|
||||
.connectedTo("foo", "bar") //
|
||||
.usingSsl() //
|
||||
.withDefaultHeaders(headers) //
|
||||
.withConnectTimeout(Duration.ofDays(1)).withSocketTimeout(Duration.ofDays(2)).build();
|
||||
.withConnectTimeout(Duration.ofDays(1)).withSocketTimeout(Duration.ofDays(2)) //
|
||||
.withProxy("localhost:8080").build();
|
||||
|
||||
assertThat(clientConfiguration.getEndpoints()).containsOnly(InetSocketAddress.createUnresolved("foo", 9200),
|
||||
InetSocketAddress.createUnresolved("bar", 9200));
|
||||
@@ -59,6 +63,7 @@ public class ClientConfigurationUnitTests {
|
||||
assertThat(clientConfiguration.getDefaultHeaders().get("foo")).containsOnly("bar");
|
||||
assertThat(clientConfiguration.getConnectTimeout()).isEqualTo(Duration.ofDays(1));
|
||||
assertThat(clientConfiguration.getSocketTimeout()).isEqualTo(Duration.ofDays(2));
|
||||
assertThat(clientConfiguration.getProxy()).contains("localhost:8080");
|
||||
}
|
||||
|
||||
@Test // DATAES-488, DATAES-504
|
||||
@@ -78,4 +83,67 @@ public class ClientConfigurationUnitTests {
|
||||
assertThat(clientConfiguration.getConnectTimeout()).isEqualTo(Duration.ofSeconds(10));
|
||||
assertThat(clientConfiguration.getSocketTimeout()).isEqualTo(Duration.ofSeconds(5));
|
||||
}
|
||||
|
||||
@Test // DATAES-607
|
||||
public void shouldAddBasicAuthenticationHeaderWhenNoHeadersAreSet() {
|
||||
|
||||
String username = "secretUser";
|
||||
String password = "secretPassword";
|
||||
|
||||
ClientConfiguration clientConfiguration = ClientConfiguration.builder() //
|
||||
.connectedTo("foo", "bar") //
|
||||
.withBasicAuth(username, password) //
|
||||
.build();
|
||||
|
||||
assertThat(clientConfiguration.getDefaultHeaders().get(HttpHeaders.AUTHORIZATION))
|
||||
.containsOnly(buildBasicAuth(username, password));
|
||||
}
|
||||
|
||||
@Test // DATAES-607
|
||||
public void shouldAddBasicAuthenticationHeaderAndKeepHeaders() {
|
||||
|
||||
String username = "secretUser";
|
||||
String password = "secretPassword";
|
||||
|
||||
HttpHeaders defaultHeaders = new HttpHeaders();
|
||||
defaultHeaders.set("foo", "bar");
|
||||
|
||||
ClientConfiguration clientConfiguration = ClientConfiguration.builder() //
|
||||
.connectedTo("foo", "bar") //
|
||||
.withBasicAuth(username, password) //
|
||||
.withDefaultHeaders(defaultHeaders) //
|
||||
.build();
|
||||
|
||||
HttpHeaders httpHeaders = clientConfiguration.getDefaultHeaders();
|
||||
|
||||
assertThat(httpHeaders.get(HttpHeaders.AUTHORIZATION)).containsOnly(buildBasicAuth(username, password));
|
||||
assertThat(httpHeaders.getFirst("foo")).isEqualTo("bar");
|
||||
assertThat(defaultHeaders.get(HttpHeaders.AUTHORIZATION)).isNull();
|
||||
}
|
||||
|
||||
@Test // DATAES-673
|
||||
public void shouldCreateSslConfigurationWithHostnameVerifier() {
|
||||
|
||||
SSLContext sslContext = mock(SSLContext.class);
|
||||
|
||||
ClientConfiguration clientConfiguration = ClientConfiguration.builder() //
|
||||
.connectedTo("foo", "bar") //
|
||||
.usingSsl(sslContext, NoopHostnameVerifier.INSTANCE) //
|
||||
.build();
|
||||
|
||||
assertThat(clientConfiguration.getEndpoints()).containsOnly(InetSocketAddress.createUnresolved("foo", 9200),
|
||||
InetSocketAddress.createUnresolved("bar", 9200));
|
||||
assertThat(clientConfiguration.useSsl()).isTrue();
|
||||
assertThat(clientConfiguration.getSslContext()).contains(sslContext);
|
||||
assertThat(clientConfiguration.getConnectTimeout()).isEqualTo(Duration.ofSeconds(10));
|
||||
assertThat(clientConfiguration.getSocketTimeout()).isEqualTo(Duration.ofSeconds(5));
|
||||
assertThat(clientConfiguration.getHostNameVerifier()).contains(NoopHostnameVerifier.INSTANCE);
|
||||
}
|
||||
|
||||
private static String buildBasicAuth(String username, String password) {
|
||||
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setBasicAuth(username, password);
|
||||
return headers.getFirst(HttpHeaders.AUTHORIZATION);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-4
@@ -17,9 +17,6 @@ package org.springframework.data.elasticsearch.client;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.elasticsearch.common.transport.TransportAddress;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
@@ -85,6 +82,6 @@ public class ClusterNodesUnitTests {
|
||||
public void rejectsUnresolvableHost() {
|
||||
|
||||
assertThatExceptionOfType(IllegalArgumentException.class) //
|
||||
.isThrownBy(() -> ClusterNodes.of("mylocalhost:80"));
|
||||
.isThrownBy(() -> ClusterNodes.of("mylocalhost.invalid.:80"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
package org.springframework.data.elasticsearch.client;
|
||||
|
||||
import static com.github.tomakehurst.wiremock.client.WireMock.*;
|
||||
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.elasticsearch.client.RequestOptions;
|
||||
import org.elasticsearch.client.RestHighLevelClient;
|
||||
|
||||
import com.github.tomakehurst.wiremock.WireMockServer;
|
||||
import com.github.tomakehurst.wiremock.client.WireMock;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* @author Peter-Josef Meisch
|
||||
*/
|
||||
public class RestClientsTest {
|
||||
|
||||
@Test
|
||||
// DATAES-700
|
||||
public void shouldUseConfiguredProxy() throws IOException {
|
||||
|
||||
WireMockServer wireMockServer = new WireMockServer(options() //
|
||||
.dynamicPort() //
|
||||
.usingFilesUnderDirectory("src/test/resources/wiremock-mappings")); // needed, otherwise Wiremock goes to
|
||||
// test/resources/mappings
|
||||
wireMockServer.start();
|
||||
try {
|
||||
WireMock.configureFor(wireMockServer.port());
|
||||
|
||||
ClientConfigurationBuilder configurationBuilder = new ClientConfigurationBuilder();
|
||||
ClientConfiguration clientConfiguration = configurationBuilder //
|
||||
.connectedTo("localhost:9200")//
|
||||
.withProxy("localhost:" + wireMockServer.port()) //
|
||||
.build();
|
||||
|
||||
RestHighLevelClient restClient = RestClients.create(clientConfiguration).rest();
|
||||
restClient.ping(RequestOptions.DEFAULT);
|
||||
|
||||
verify(headRequestedFor(urlEqualTo("/")));
|
||||
|
||||
} finally {
|
||||
wireMockServer.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+2
-3
@@ -18,7 +18,6 @@ package org.springframework.data.elasticsearch.client.reactive;
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import org.springframework.data.elasticsearch.client.reactive.HostProvider.Verification;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
@@ -26,6 +25,7 @@ import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.data.elasticsearch.client.ElasticsearchHost;
|
||||
import org.springframework.data.elasticsearch.client.ElasticsearchHost.State;
|
||||
import org.springframework.data.elasticsearch.client.reactive.HostProvider.Verification;
|
||||
import org.springframework.data.elasticsearch.client.reactive.ReactiveMockClientTestsUtils.MockDelegatingElasticsearchHostProvider;
|
||||
import org.springframework.data.elasticsearch.client.reactive.ReactiveMockClientTestsUtils.MockWebClientProvider.Receive;
|
||||
import org.springframework.web.reactive.function.client.ClientResponse;
|
||||
@@ -106,8 +106,7 @@ public class MultiNodeHostProviderUnitTests {
|
||||
|
||||
provider.clusterInfo().as(StepVerifier::create).expectNextCount(1).verifyComplete();
|
||||
|
||||
provider.getActive(Verification.ACTIVE).as(StepVerifier::create).expectNext(mock.client(HOST_2))
|
||||
.verifyComplete();
|
||||
provider.getActive(Verification.ACTIVE).as(StepVerifier::create).expectNext(mock.client(HOST_2)).verifyComplete();
|
||||
|
||||
verify(mock.client(HOST_2), times(2)).head();
|
||||
}
|
||||
|
||||
+29
-2
@@ -22,6 +22,7 @@ import reactor.test.StepVerifier;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.Duration;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
@@ -32,6 +33,7 @@ import org.elasticsearch.ElasticsearchStatusException;
|
||||
import org.elasticsearch.Version;
|
||||
import org.elasticsearch.action.admin.indices.create.CreateIndexRequest;
|
||||
import org.elasticsearch.action.admin.indices.get.GetIndexRequest;
|
||||
import org.elasticsearch.action.bulk.BulkRequest;
|
||||
import org.elasticsearch.action.delete.DeleteRequest;
|
||||
import org.elasticsearch.action.get.GetRequest;
|
||||
import org.elasticsearch.action.get.MultiGetRequest;
|
||||
@@ -53,7 +55,6 @@ import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.data.elasticsearch.ElasticsearchVersion;
|
||||
import org.springframework.data.elasticsearch.ElasticsearchVersionRule;
|
||||
import org.springframework.data.elasticsearch.TestUtils;
|
||||
@@ -66,7 +67,7 @@ import org.springframework.test.context.junit4.SpringRunner;
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
* @currentRead Fool's Fate - Robin Hobb
|
||||
* @author Henrique Amaral
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@ContextConfiguration("classpath:infrastructure.xml")
|
||||
@@ -654,6 +655,32 @@ public class ReactiveElasticsearchClientTests {
|
||||
.verifyError(ElasticsearchStatusException.class);
|
||||
}
|
||||
|
||||
@Test // DATAES-684
|
||||
public void bulkShouldUpdateExistingDocument() {
|
||||
String idFirstDoc = addSourceDocument().ofType(TYPE_I).to(INDEX_I);
|
||||
String idSecondDoc = addSourceDocument().ofType(TYPE_I).to(INDEX_I);
|
||||
|
||||
UpdateRequest requestFirstDoc = new UpdateRequest(INDEX_I, TYPE_I, idFirstDoc) //
|
||||
.doc(Collections.singletonMap("dutiful", "farseer"));
|
||||
UpdateRequest requestSecondDoc = new UpdateRequest(INDEX_I, TYPE_I, idSecondDoc) //
|
||||
.doc(Collections.singletonMap("secondDocUpdate", "secondDocUpdatePartTwo"));
|
||||
|
||||
BulkRequest bulkRequest = new BulkRequest();
|
||||
bulkRequest.add(requestFirstDoc);
|
||||
bulkRequest.add(requestSecondDoc);
|
||||
|
||||
client.bulk(bulkRequest).as(StepVerifier::create) //
|
||||
.consumeNextWith(it -> {
|
||||
assertThat(it.status()).isEqualTo(RestStatus.OK);
|
||||
assertThat(it.hasFailures()).isFalse();
|
||||
|
||||
Arrays.stream(it.getItems()).forEach(itemResponse -> {
|
||||
assertThat(itemResponse.status()).isEqualTo(RestStatus.OK);
|
||||
assertThat(itemResponse.getVersion()).isEqualTo(2);
|
||||
});
|
||||
}).verifyComplete();
|
||||
}
|
||||
|
||||
private AddToIndexOfType addSourceDocument() {
|
||||
return add(DOC_SOURCE);
|
||||
}
|
||||
|
||||
+25
-2
@@ -16,10 +16,11 @@
|
||||
package org.springframework.data.elasticsearch.client.reactive;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
import static org.springframework.data.elasticsearch.client.reactive.ReactiveMockClientTestsUtils.MockWebClientProvider.Receive.*;
|
||||
|
||||
import org.elasticsearch.rest.RestStatus;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
@@ -29,6 +30,7 @@ import java.util.Collections;
|
||||
|
||||
import org.elasticsearch.ElasticsearchStatusException;
|
||||
import org.elasticsearch.action.DocWriteResponse.Result;
|
||||
import org.elasticsearch.action.bulk.BulkRequest;
|
||||
import org.elasticsearch.action.delete.DeleteRequest;
|
||||
import org.elasticsearch.action.get.GetRequest;
|
||||
import org.elasticsearch.action.get.MultiGetRequest;
|
||||
@@ -51,7 +53,7 @@ import org.springframework.util.StreamUtils;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @currentRead Golden Fool - Robin Hobb
|
||||
* @author Henrique Amaral
|
||||
*/
|
||||
public class ReactiveElasticsearchClientUnitTests {
|
||||
|
||||
@@ -623,4 +625,25 @@ public class ReactiveElasticsearchClientUnitTests {
|
||||
});
|
||||
}
|
||||
|
||||
@Test // DATAES-684
|
||||
public void bulkShouldEmitResponseCorrectly() {
|
||||
|
||||
hostProvider.when(HOST) //
|
||||
.receiveBulkOk();
|
||||
|
||||
final UpdateRequest updateRequest = new UpdateRequest("twitter", "doc", "1")
|
||||
.doc(Collections.singletonMap("user", "cstrobl"));
|
||||
final BulkRequest bulkRequest = new BulkRequest();
|
||||
bulkRequest.add(updateRequest);
|
||||
|
||||
client.bulk(bulkRequest).as(StepVerifier::create) //
|
||||
.consumeNextWith(bulkResponse -> {
|
||||
|
||||
assertThat(bulkResponse.status()).isEqualTo(RestStatus.OK);
|
||||
assertThat(bulkResponse.hasFailures()).isFalse();
|
||||
|
||||
}) //
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+12
-4
@@ -17,10 +17,6 @@ package org.springframework.data.elasticsearch.client.reactive;
|
||||
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.web.util.DefaultUriBuilderFactory;
|
||||
import org.springframework.web.util.UriBuilder;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.io.IOException;
|
||||
@@ -41,12 +37,15 @@ import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.data.elasticsearch.client.ElasticsearchHost;
|
||||
import org.springframework.data.elasticsearch.client.reactive.ReactiveMockClientTestsUtils.MockWebClientProvider.Send;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.lang.Nullable;
|
||||
@@ -56,9 +55,12 @@ import org.springframework.web.reactive.function.client.ClientResponse;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
import org.springframework.web.reactive.function.client.WebClient.RequestBodyUriSpec;
|
||||
import org.springframework.web.reactive.function.client.WebClient.RequestHeadersUriSpec;
|
||||
import org.springframework.web.util.DefaultUriBuilderFactory;
|
||||
import org.springframework.web.util.UriBuilder;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @author Henrique Amaral
|
||||
*/
|
||||
public class ReactiveMockClientTestsUtils {
|
||||
|
||||
@@ -360,6 +362,12 @@ public class ReactiveMockClientTestsUtils {
|
||||
});
|
||||
}
|
||||
|
||||
default Receive receiveBulkOk() {
|
||||
|
||||
return receiveJsonFromFile("bulk-ok") //
|
||||
.receive(Receive::ok);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public interface Receive {
|
||||
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright 2018-2019 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.util;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import org.elasticsearch.action.delete.DeleteRequest;
|
||||
import org.elasticsearch.action.index.IndexRequest;
|
||||
import org.elasticsearch.client.Request;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link RequestConverters}.
|
||||
*
|
||||
* @author Roman Puchkovskiy
|
||||
*/
|
||||
public class RequestConvertersTest {
|
||||
@Test // DATAES-652
|
||||
public void shouldNotAddIfSeqNoAndIfPrimaryTermToResultIfInputDoesNotcontainThemWhenConvertingIndexRequest() {
|
||||
IndexRequest request = createMinimalIndexRequest();
|
||||
|
||||
Request result = RequestConverters.index(request);
|
||||
|
||||
assertThat(result.getParameters(), not(hasKey("if_seq_no")));
|
||||
assertThat(result.getParameters(), not(hasKey("if_primary_term")));
|
||||
}
|
||||
|
||||
private IndexRequest createMinimalIndexRequest() {
|
||||
IndexRequest request = new IndexRequest("the-index", "the-type", "id");
|
||||
request.source(new HashMap<String, String>() {
|
||||
{
|
||||
put("test", "test");
|
||||
}
|
||||
});
|
||||
return request;
|
||||
}
|
||||
|
||||
@Test // DATAES-652
|
||||
public void shouldAddIfSeqNoAndIfPrimaryTermToResultIfInputcontainsThemWhenConvertingIndexRequest() {
|
||||
IndexRequest request = createMinimalIndexRequest();
|
||||
request.setIfSeqNo(3);
|
||||
request.setIfPrimaryTerm(4);
|
||||
|
||||
Request result = RequestConverters.index(request);
|
||||
|
||||
assertThat(result.getParameters(), hasEntry("if_seq_no", "3"));
|
||||
assertThat(result.getParameters(), hasEntry("if_primary_term", "4"));
|
||||
}
|
||||
|
||||
@Test // DATAES-652
|
||||
public void shouldNotAddIfSeqNoAndIfPrimaryTermToResultIfInputDoesNotcontainThemWhenConvertingDeleteRequest() {
|
||||
DeleteRequest request = createMinimalDeleteRequest();
|
||||
|
||||
Request result = RequestConverters.delete(request);
|
||||
|
||||
assertThat(result.getParameters(), not(hasKey("if_seq_no")));
|
||||
assertThat(result.getParameters(), not(hasKey("if_primary_term")));
|
||||
}
|
||||
|
||||
private DeleteRequest createMinimalDeleteRequest() {
|
||||
return new DeleteRequest("the-index", "the-type", "id");
|
||||
}
|
||||
|
||||
@Test // DATAES-652
|
||||
public void shouldAddIfSeqNoAndIfPrimaryTermToResultIfInputcontainsThemWhenConvertingDeleteRequest() {
|
||||
DeleteRequest request = createMinimalDeleteRequest();
|
||||
request.setIfSeqNo(3);
|
||||
request.setIfPrimaryTerm(4);
|
||||
|
||||
Request result = RequestConverters.delete(request);
|
||||
|
||||
assertThat(result.getParameters(), hasEntry("if_seq_no", "3"));
|
||||
assertThat(result.getParameters(), hasEntry("if_primary_term", "4"));
|
||||
}
|
||||
}
|
||||
-1
@@ -24,7 +24,6 @@ import java.util.Collections;
|
||||
import org.apache.commons.lang.ClassUtils;
|
||||
import org.elasticsearch.client.RestHighLevelClient;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
-101
@@ -1,101 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-15 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 static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.elasticsearch.node.NodeValidationException;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.elasticsearch.Utils;
|
||||
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
|
||||
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
|
||||
import org.springframework.data.elasticsearch.entities.SampleEntity;
|
||||
import org.springframework.data.elasticsearch.repositories.sample.SampleElasticsearchRepository;
|
||||
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
|
||||
import org.springframework.data.elasticsearch.repository.config.EnableElasticsearchRepositories;
|
||||
import org.springframework.data.repository.Repository;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
/**
|
||||
* @author Rizwan Idrees
|
||||
* @author Mohsin Husen
|
||||
* @author Kevin Leturc
|
||||
* @author Gad Akuka
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration
|
||||
public class EnableElasticsearchRepositoriesTests implements ApplicationContextAware {
|
||||
|
||||
ApplicationContext context;
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
this.context = applicationContext;
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableElasticsearchRepositories(basePackages = {"org.springframework.data.elasticsearch.repositories.sample",
|
||||
"org.springframework.data.elasticsearch.config"})
|
||||
static class Config {
|
||||
|
||||
@Bean
|
||||
public ElasticsearchOperations elasticsearchTemplate() throws NodeValidationException {
|
||||
return new ElasticsearchTemplate(Utils.getNodeClient());
|
||||
}
|
||||
}
|
||||
|
||||
@Autowired
|
||||
private SampleElasticsearchRepository repository;
|
||||
|
||||
@Autowired(required = false)
|
||||
private SampleRepository nestedRepository;
|
||||
|
||||
interface SampleRepository extends Repository<SampleEntity, Long> {};
|
||||
|
||||
@Test
|
||||
public void bootstrapsRepository() {
|
||||
assertThat(repository, is(notNullValue()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldScanSelectedPackage() {
|
||||
//given
|
||||
|
||||
//when
|
||||
String[] beanNamesForType = context.getBeanNamesForType(ElasticsearchRepository.class);
|
||||
|
||||
//then
|
||||
assertThat(beanNamesForType.length, is(2));
|
||||
assertTrue(Arrays.asList(beanNamesForType).contains("sampleElasticsearchRepository"));
|
||||
assertTrue(Arrays.asList(beanNamesForType).contains("sampleUUIDKeyedElasticsearchRepository"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hasNotNestedRepository() {
|
||||
assertNull(nestedRepository);
|
||||
}
|
||||
}
|
||||
+20
-10
@@ -13,29 +13,30 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.config;
|
||||
package org.springframework.data.elasticsearch.config.abstractelasticsearchconfiguration;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import org.elasticsearch.client.RestHighLevelClient;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.elasticsearch.repositories.existing.index.CreateIndexFalseRepository;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.elasticsearch.annotations.Document;
|
||||
import org.springframework.data.elasticsearch.config.AbstractElasticsearchConfiguration;
|
||||
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
|
||||
import org.springframework.data.elasticsearch.repository.config.EnableElasticsearchRepositories;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
/**
|
||||
* Tests for {@link AbstractElasticsearchConfiguration}.
|
||||
*
|
||||
* @author Peter-Josef Meisch
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@RunWith(SpringRunner.class)
|
||||
@ContextConfiguration
|
||||
public class ElasticsearchConfigurationTests {
|
||||
|
||||
@@ -47,8 +48,8 @@ public class ElasticsearchConfigurationTests {
|
||||
|
||||
@Configuration
|
||||
@EnableElasticsearchRepositories(
|
||||
basePackages = { "org.springframework.data.elasticsearch.repositories.existing.index",
|
||||
"org.springframework.data.elasticsearch.config" })
|
||||
basePackages = { "org.springframework.data.elasticsearch.config.abstractelasticsearchconfiguration" },
|
||||
considerNestedRepositories = true)
|
||||
static class Config extends AbstractElasticsearchConfiguration {
|
||||
|
||||
@Override
|
||||
@@ -60,6 +61,15 @@ public class ElasticsearchConfigurationTests {
|
||||
|
||||
@Test // DATAES-563
|
||||
public void bootstrapsRepository() {
|
||||
assertThat(repository, is(notNullValue()));
|
||||
|
||||
assertThat(repository).isNotNull();
|
||||
}
|
||||
|
||||
@Document(indexName = "test-index-config-abstractelasticsearchconfiguraiton", type = "test-type", createIndex = false)
|
||||
static class CreateIndexFalseEntity {
|
||||
|
||||
@Id private String id;
|
||||
}
|
||||
|
||||
interface CreateIndexFalseRepository extends ElasticsearchRepository<CreateIndexFalseEntity, String> {}
|
||||
}
|
||||
+27
-17
@@ -13,51 +13,61 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.config;
|
||||
package org.springframework.data.elasticsearch.config.namespace;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.hamcrest.core.Is.is;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.elasticsearch.annotations.Document;
|
||||
import org.springframework.data.elasticsearch.client.RestClientFactoryBean;
|
||||
import org.springframework.data.elasticsearch.client.TransportClientFactoryBean;
|
||||
import org.springframework.data.elasticsearch.repositories.sample.SampleElasticsearchRepository;
|
||||
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
/**
|
||||
* @author Rizwan Idrees
|
||||
* @author Mohsin Husen
|
||||
* @author Don Wellington
|
||||
* @author Peter-Josef Meisch
|
||||
*/
|
||||
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@RunWith(SpringRunner.class)
|
||||
@ContextConfiguration("namespace.xml")
|
||||
public class ElasticsearchNamespaceHandlerTests {
|
||||
|
||||
@Autowired
|
||||
private ApplicationContext context;
|
||||
@Autowired private ApplicationContext context;
|
||||
|
||||
@Test
|
||||
public void shouldCreateTransportClient() {
|
||||
assertThat(context.getBean(TransportClientFactoryBean.class), is(notNullValue()));
|
||||
assertThat(context.getBean(TransportClientFactoryBean.class), is(instanceOf(TransportClientFactoryBean.class)));
|
||||
|
||||
assertThat(context.getBean(TransportClientFactoryBean.class)).isNotNull();
|
||||
assertThat(context.getBean(TransportClientFactoryBean.class)).isInstanceOf(TransportClientFactoryBean.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCreateRepository() {
|
||||
assertThat(context.getBean(TransportClientFactoryBean.class), is(notNullValue()));
|
||||
assertThat(context.getBean(SampleElasticsearchRepository.class),
|
||||
is(instanceOf(SampleElasticsearchRepository.class)));
|
||||
|
||||
assertThat(context.getBean(TransportClientFactoryBean.class)).isNotNull();
|
||||
assertThat(context.getBean(CreateIndexFalseRepository.class)).isInstanceOf(CreateIndexFalseRepository.class);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void shouldCreateRestClient() {
|
||||
assertThat(context.getBean(RestClientFactoryBean.class), is(notNullValue()));
|
||||
assertThat(context.getBean(RestClientFactoryBean.class), is(instanceOf(RestClientFactoryBean.class)));
|
||||
|
||||
assertThat(context.getBean(RestClientFactoryBean.class)).isNotNull();
|
||||
assertThat(context.getBean(RestClientFactoryBean.class)).isInstanceOf(RestClientFactoryBean.class);
|
||||
}
|
||||
|
||||
@Document(indexName = "test-index-config-namespace", type = "test-type", createIndex = false)
|
||||
static class CreateIndexFalseEntity {
|
||||
|
||||
@Id private String id;
|
||||
}
|
||||
|
||||
interface CreateIndexFalseRepository extends ElasticsearchRepository<CreateIndexFalseEntity, String> {}
|
||||
}
|
||||
+47
-12
@@ -13,50 +13,85 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.config;
|
||||
package org.springframework.data.elasticsearch.config.nested;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.springframework.data.elasticsearch.annotations.FieldType.*;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
import java.lang.Double;
|
||||
import java.lang.Long;
|
||||
|
||||
import org.elasticsearch.node.NodeValidationException;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.annotation.Version;
|
||||
import org.springframework.data.elasticsearch.Utils;
|
||||
import org.springframework.data.elasticsearch.annotations.Document;
|
||||
import org.springframework.data.elasticsearch.annotations.Field;
|
||||
import org.springframework.data.elasticsearch.annotations.Score;
|
||||
import org.springframework.data.elasticsearch.annotations.ScriptedField;
|
||||
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
|
||||
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
|
||||
import org.springframework.data.elasticsearch.entities.SampleEntity;
|
||||
import org.springframework.data.elasticsearch.core.geo.GeoPoint;
|
||||
import org.springframework.data.elasticsearch.repository.config.EnableElasticsearchRepositories;
|
||||
import org.springframework.data.repository.Repository;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
/**
|
||||
* @author Kevin Leturc
|
||||
* @author Peter-Josef Meisch
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@RunWith(SpringRunner.class)
|
||||
@ContextConfiguration
|
||||
public class EnableNestedElasticsearchRepositoriesTests {
|
||||
|
||||
@Configuration
|
||||
@EnableElasticsearchRepositories(basePackages = {"org.springframework.data.elasticsearch.repositories.sample",
|
||||
"org.springframework.data.elasticsearch.config"}, considerNestedRepositories = true)
|
||||
@EnableElasticsearchRepositories(basePackages = { "org.springframework.data.elasticsearch.config.nested" },
|
||||
considerNestedRepositories = true)
|
||||
static class Config {
|
||||
|
||||
@Bean
|
||||
public ElasticsearchOperations elasticsearchTemplate() throws NodeValidationException {
|
||||
return new ElasticsearchTemplate(Utils.getNodeClient());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Autowired(required = false)
|
||||
private SampleRepository nestedRepository;
|
||||
|
||||
interface SampleRepository extends Repository<SampleEntity, Long> {};
|
||||
@Autowired(required = false) private SampleRepository nestedRepository;
|
||||
|
||||
@Test
|
||||
public void hasNestedRepository() {
|
||||
assertNotNull(nestedRepository);
|
||||
assertThat(nestedRepository).isNotNull();
|
||||
}
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@Document(indexName = "test-index-sample-config-nested", type = "test-type", shards = 1, replicas = 0,
|
||||
refreshInterval = "-1")
|
||||
static class SampleEntity {
|
||||
|
||||
@Id private String id;
|
||||
@Field(type = Text, store = true, fielddata = true) private String type;
|
||||
@Field(type = Text, store = true, fielddata = true) private String message;
|
||||
private int rate;
|
||||
@ScriptedField private Double scriptedRate;
|
||||
private boolean available;
|
||||
private String highlightedMessage;
|
||||
private GeoPoint location;
|
||||
@Version private Long version;
|
||||
|
||||
@Score private float score;
|
||||
|
||||
}
|
||||
|
||||
interface SampleRepository extends Repository<SampleEntity, Long> {}
|
||||
}
|
||||
+156
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* Copyright 2013-15 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.notnested;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.springframework.data.elasticsearch.annotations.FieldType.*;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.lang.Double;
|
||||
import java.lang.Long;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.elasticsearch.node.NodeValidationException;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.annotation.Version;
|
||||
import org.springframework.data.elasticsearch.Utils;
|
||||
import org.springframework.data.elasticsearch.annotations.Document;
|
||||
import org.springframework.data.elasticsearch.annotations.Field;
|
||||
import org.springframework.data.elasticsearch.annotations.FieldType;
|
||||
import org.springframework.data.elasticsearch.annotations.Score;
|
||||
import org.springframework.data.elasticsearch.annotations.ScriptedField;
|
||||
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
|
||||
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
|
||||
import org.springframework.data.elasticsearch.core.geo.GeoPoint;
|
||||
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
|
||||
import org.springframework.data.elasticsearch.repository.config.EnableElasticsearchRepositories;
|
||||
import org.springframework.data.elasticsearch.utils.IndexInitializer;
|
||||
import org.springframework.data.repository.Repository;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
/**
|
||||
* @author Rizwan Idrees
|
||||
* @author Mohsin Husen
|
||||
* @author Kevin Leturc
|
||||
* @author Gad Akuka
|
||||
* @author Peter-Josef Meisch
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@ContextConfiguration
|
||||
public class EnableElasticsearchRepositoriesTests implements ApplicationContextAware {
|
||||
|
||||
ApplicationContext context;
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
|
||||
this.context = applicationContext;
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableElasticsearchRepositories(basePackages = { "org.springframework.data.elasticsearch.config.notnested" })
|
||||
static class Config {
|
||||
|
||||
@Bean
|
||||
public ElasticsearchOperations elasticsearchTemplate() throws NodeValidationException {
|
||||
|
||||
return new ElasticsearchTemplate(Utils.getNodeClient());
|
||||
}
|
||||
}
|
||||
|
||||
@Autowired ElasticsearchTemplate elasticsearchTemplate;
|
||||
|
||||
@Autowired private SampleElasticsearchRepository repository;
|
||||
|
||||
@Autowired(required = false) private SampleRepository nestedRepository;
|
||||
|
||||
interface SampleRepository extends Repository<EnableElasticsearchRepositoriesTests.SampleEntity, Long> {}
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
IndexInitializer.init(elasticsearchTemplate, SampleEntity.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bootstrapsRepository() {
|
||||
|
||||
assertThat(repository).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldScanSelectedPackage() {
|
||||
|
||||
// given
|
||||
|
||||
// when
|
||||
String[] beanNamesForType = context.getBeanNamesForType(ElasticsearchRepository.class);
|
||||
|
||||
// then
|
||||
assertThat(beanNamesForType).containsExactlyInAnyOrder("sampleElasticsearchRepository",
|
||||
"sampleUUIDKeyedElasticsearchRepository");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hasNotNestedRepository() {
|
||||
|
||||
assertThat(nestedRepository).isNull();
|
||||
}
|
||||
|
||||
@Data
|
||||
@Document(indexName = "test-index-sample-config-not-nested", type = "test-type", shards = 1, replicas = 0,
|
||||
refreshInterval = "-1")
|
||||
static class SampleEntity {
|
||||
|
||||
@Id private String id;
|
||||
@Field(type = Text, store = true, fielddata = true) private String type;
|
||||
@Field(type = Text, store = true, fielddata = true) private String message;
|
||||
private int rate;
|
||||
@ScriptedField private Double scriptedRate;
|
||||
private boolean available;
|
||||
private String highlightedMessage;
|
||||
private GeoPoint location;
|
||||
@Version private Long version;
|
||||
@Score private float score;
|
||||
}
|
||||
|
||||
@Data
|
||||
@Document(indexName = "test-index-uuid-keyed-config-not-nested", type = "test-type-uuid-keyed", shards = 1,
|
||||
replicas = 0, refreshInterval = "-1")
|
||||
static class SampleEntityUUIDKeyed {
|
||||
|
||||
@Id private UUID id;
|
||||
private String type;
|
||||
@Field(type = FieldType.Text, fielddata = true) private String message;
|
||||
private int rate;
|
||||
@ScriptedField private Long scriptedRate;
|
||||
private boolean available;
|
||||
private String highlightedMessage;
|
||||
private GeoPoint location;
|
||||
@Version private Long version;
|
||||
}
|
||||
}
|
||||
+5
-5
@@ -13,11 +13,10 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.repositories.sample;
|
||||
package org.springframework.data.elasticsearch.config.notnested;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.elasticsearch.entities.SampleEntity;
|
||||
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
|
||||
|
||||
/**
|
||||
@@ -25,13 +24,14 @@ import org.springframework.data.elasticsearch.repository.ElasticsearchRepository
|
||||
* @author Mohsin Husen
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public interface SampleElasticsearchRepository extends ElasticsearchRepository<SampleEntity, String> {
|
||||
public interface SampleElasticsearchRepository
|
||||
extends ElasticsearchRepository<EnableElasticsearchRepositoriesTests.SampleEntity, String> {
|
||||
|
||||
long deleteSampleEntityById(String id);
|
||||
|
||||
List<SampleEntity> deleteByAvailable(boolean available);
|
||||
List<EnableElasticsearchRepositoriesTests.SampleEntity> deleteByAvailable(boolean available);
|
||||
|
||||
List<SampleEntity> deleteByMessage(String message);
|
||||
List<EnableElasticsearchRepositoriesTests.SampleEntity> deleteByMessage(String message);
|
||||
|
||||
void deleteByType(String type);
|
||||
|
||||
+6
-6
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* Copyright 2018-2019 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.
|
||||
@@ -13,25 +13,25 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.repositories.sample;
|
||||
package org.springframework.data.elasticsearch.config.notnested;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.springframework.data.elasticsearch.entities.SampleEntityUUIDKeyed;
|
||||
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
|
||||
|
||||
/**
|
||||
* @author Gad Akuka
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public interface SampleUUIDKeyedElasticsearchRepository extends ElasticsearchRepository<SampleEntityUUIDKeyed, UUID> {
|
||||
interface SampleUUIDKeyedElasticsearchRepository
|
||||
extends ElasticsearchRepository<EnableElasticsearchRepositoriesTests.SampleEntityUUIDKeyed, UUID> {
|
||||
|
||||
long deleteSampleEntityUUIDKeyedById(UUID id);
|
||||
|
||||
List<SampleEntityUUIDKeyed> deleteByAvailable(boolean available);
|
||||
List<EnableElasticsearchRepositoriesTests.SampleEntityUUIDKeyed> deleteByAvailable(boolean available);
|
||||
|
||||
List<SampleEntityUUIDKeyed> deleteByMessage(String message);
|
||||
List<EnableElasticsearchRepositoriesTests.SampleEntityUUIDKeyed> deleteByMessage(String message);
|
||||
|
||||
void deleteByType(String type);
|
||||
|
||||
@@ -1,211 +0,0 @@
|
||||
/*
|
||||
* Copyright 2014-2019 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.apache.commons.lang.RandomStringUtils.*;
|
||||
import static org.elasticsearch.index.query.QueryBuilders.*;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.elasticsearch.cluster.metadata.AliasMetaData;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.elasticsearch.core.query.AliasBuilder;
|
||||
import org.springframework.data.elasticsearch.core.query.AliasQuery;
|
||||
import org.springframework.data.elasticsearch.core.query.IndexQuery;
|
||||
import org.springframework.data.elasticsearch.core.query.IndexQueryBuilder;
|
||||
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
|
||||
import org.springframework.data.elasticsearch.core.query.SearchQuery;
|
||||
import org.springframework.data.elasticsearch.entities.SampleEntity;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
/**
|
||||
* @author Mohsin Husen
|
||||
* @author Ilkang Na
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration("classpath:elasticsearch-template-test.xml")
|
||||
public class AliasTests {
|
||||
|
||||
private static final String INDEX_NAME_1 = "test-alias-index-1";
|
||||
private static final String INDEX_NAME_2 = "test-alias-index-2";
|
||||
private static final String TYPE_NAME = "test-alias-type";
|
||||
|
||||
@Autowired
|
||||
private ElasticsearchTemplate elasticsearchTemplate;
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
Map<String, Object> settings = new HashMap<>();
|
||||
settings.put("index.refresh_interval", "-1");
|
||||
settings.put("index.number_of_replicas", "0");
|
||||
settings.put("index.number_of_shards", "2");
|
||||
settings.put("index.store.type", "fs");
|
||||
|
||||
elasticsearchTemplate.deleteIndex(INDEX_NAME_1);
|
||||
elasticsearchTemplate.createIndex(INDEX_NAME_1, settings);
|
||||
elasticsearchTemplate.refresh(INDEX_NAME_1);
|
||||
|
||||
elasticsearchTemplate.deleteIndex(INDEX_NAME_2);
|
||||
elasticsearchTemplate.createIndex(INDEX_NAME_2, settings);
|
||||
elasticsearchTemplate.refresh(INDEX_NAME_2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldAddAlias() {
|
||||
// given
|
||||
String aliasName = "test-alias";
|
||||
AliasQuery aliasQuery = new AliasBuilder()
|
||||
.withIndexName(INDEX_NAME_1)
|
||||
.withAliasName(aliasName).build();
|
||||
// when
|
||||
elasticsearchTemplate.addAlias(aliasQuery);
|
||||
// then
|
||||
List<AliasMetaData> aliases = elasticsearchTemplate.queryForAlias(INDEX_NAME_1);
|
||||
assertThat(aliases, is(notNullValue()));
|
||||
assertThat(aliases.get(0).alias(), is(aliasName));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldRemoveAlias() {
|
||||
// given
|
||||
String indexName = INDEX_NAME_1;
|
||||
String aliasName = "test-alias";
|
||||
AliasQuery aliasQuery = new AliasBuilder()
|
||||
.withIndexName(indexName)
|
||||
.withAliasName(aliasName).build();
|
||||
// when
|
||||
elasticsearchTemplate.addAlias(aliasQuery);
|
||||
List<AliasMetaData> aliases = elasticsearchTemplate.queryForAlias(indexName);
|
||||
assertThat(aliases, is(notNullValue()));
|
||||
assertThat(aliases.get(0).alias(), is(aliasName));
|
||||
// then
|
||||
elasticsearchTemplate.removeAlias(aliasQuery);
|
||||
aliases = elasticsearchTemplate.queryForAlias(indexName);
|
||||
assertThat(aliases, anyOf(is(nullValue()), hasSize(0)));
|
||||
}
|
||||
|
||||
/*
|
||||
DATAES-70
|
||||
*/
|
||||
@Test
|
||||
public void shouldAddAliasWithGivenRoutingValue() {
|
||||
//given
|
||||
String indexName = INDEX_NAME_1;
|
||||
String alias = "test-alias";
|
||||
|
||||
AliasQuery aliasQuery = new AliasBuilder()
|
||||
.withIndexName(indexName)
|
||||
.withAliasName(alias)
|
||||
.withRouting("0").build();
|
||||
|
||||
//when
|
||||
elasticsearchTemplate.addAlias(aliasQuery);
|
||||
|
||||
String documentId = randomNumeric(5);
|
||||
SampleEntity sampleEntity = SampleEntity.builder().id(documentId)
|
||||
.message("some message")
|
||||
.version(System.currentTimeMillis()).build();
|
||||
|
||||
IndexQuery indexQuery = new IndexQueryBuilder()
|
||||
.withIndexName(alias)
|
||||
.withId(sampleEntity.getId())
|
||||
.withType(TYPE_NAME)
|
||||
.withObject(sampleEntity)
|
||||
.build();
|
||||
|
||||
elasticsearchTemplate.index(indexQuery);
|
||||
elasticsearchTemplate.refresh(INDEX_NAME_1);
|
||||
|
||||
SearchQuery query = new NativeSearchQueryBuilder().withQuery(matchAllQuery())
|
||||
.withIndices(alias).withTypes(TYPE_NAME).build();
|
||||
long count = elasticsearchTemplate.count(query);
|
||||
//then
|
||||
List<AliasMetaData> aliases = elasticsearchTemplate.queryForAlias(INDEX_NAME_1);
|
||||
assertThat(aliases, is(notNullValue()));
|
||||
assertThat(aliases.get(0).alias(), is(alias));
|
||||
assertThat(aliases.get(0).searchRouting(), is("0"));
|
||||
assertThat(aliases.get(0).indexRouting(), is("0"));
|
||||
assertThat(count, is(1L));
|
||||
|
||||
//cleanup
|
||||
elasticsearchTemplate.removeAlias(aliasQuery);
|
||||
elasticsearchTemplate.deleteIndex(SampleEntity.class);
|
||||
elasticsearchTemplate.createIndex(SampleEntity.class);
|
||||
elasticsearchTemplate.putMapping(SampleEntity.class);
|
||||
elasticsearchTemplate.refresh(SampleEntity.class);
|
||||
}
|
||||
|
||||
/*
|
||||
DATAES-70
|
||||
*/
|
||||
@Test
|
||||
public void shouldAddAliasForVariousRoutingValues() {
|
||||
//given
|
||||
String alias1 = "test-alias-1";
|
||||
String alias2 = "test-alias-2";
|
||||
|
||||
AliasQuery aliasQuery1 = new AliasBuilder()
|
||||
.withIndexName(INDEX_NAME_1)
|
||||
.withAliasName(alias1)
|
||||
.withIndexRouting("0").build();
|
||||
|
||||
AliasQuery aliasQuery2 = new AliasBuilder()
|
||||
.withIndexName(INDEX_NAME_2)
|
||||
.withAliasName(alias2)
|
||||
.withSearchRouting("1").build();
|
||||
|
||||
//when
|
||||
elasticsearchTemplate.addAlias(aliasQuery1);
|
||||
elasticsearchTemplate.addAlias(aliasQuery2);
|
||||
|
||||
String documentId = randomNumeric(5);
|
||||
SampleEntity sampleEntity = SampleEntity.builder().id(documentId)
|
||||
.message("some message")
|
||||
.version(System.currentTimeMillis()).build();
|
||||
|
||||
IndexQuery indexQuery = new IndexQueryBuilder()
|
||||
.withIndexName(alias1)
|
||||
.withType(TYPE_NAME)
|
||||
.withId(sampleEntity.getId())
|
||||
.withObject(sampleEntity).build();
|
||||
|
||||
elasticsearchTemplate.index(indexQuery);
|
||||
|
||||
|
||||
// then
|
||||
List<AliasMetaData> responseAlias1 = elasticsearchTemplate.queryForAlias(INDEX_NAME_1);
|
||||
assertThat(responseAlias1, is(notNullValue()));
|
||||
assertThat(responseAlias1.get(0).alias(), is(alias1));
|
||||
assertThat(responseAlias1.get(0).indexRouting(), is("0"));
|
||||
|
||||
List<AliasMetaData> responseAlias2 = elasticsearchTemplate.queryForAlias(INDEX_NAME_2);
|
||||
assertThat(responseAlias2, is(notNullValue()));
|
||||
assertThat(responseAlias2.get(0).alias(), is(alias2));
|
||||
assertThat(responseAlias2.get(0).searchRouting(), is("1"));
|
||||
|
||||
//cleanup
|
||||
elasticsearchTemplate.removeAlias(aliasQuery1);
|
||||
elasticsearchTemplate.removeAlias(aliasQuery2);
|
||||
}
|
||||
}
|
||||
@@ -25,18 +25,18 @@ import java.util.Map;
|
||||
public class CustomEntityMapper implements EntityMapper {
|
||||
|
||||
public CustomEntityMapper() {
|
||||
//custom configuration/implementation (e.g. FasterXML/jackson)
|
||||
// custom configuration/implementation (e.g. FasterXML/jackson)
|
||||
}
|
||||
|
||||
@Override
|
||||
public String mapToString(Object object) throws IOException {
|
||||
//mapping Object to text
|
||||
// mapping Object to text
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T mapToObject(String source, Class<T> clazz) throws IOException {
|
||||
//mapping text to Object
|
||||
// mapping text to Object
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,11 +15,12 @@
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.core;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import org.elasticsearch.action.get.GetResponse;
|
||||
import org.elasticsearch.action.get.MultiGetResponse;
|
||||
import org.elasticsearch.action.search.SearchResponse;
|
||||
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
|
||||
|
||||
@@ -29,7 +30,6 @@ import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
|
||||
*/
|
||||
public class CustomResultMapper implements ResultsMapper {
|
||||
|
||||
|
||||
private EntityMapper entityMapper;
|
||||
|
||||
public CustomResultMapper(EntityMapper entityMapper) {
|
||||
@@ -43,16 +43,16 @@ public class CustomResultMapper implements ResultsMapper {
|
||||
|
||||
@Override
|
||||
public <T> T mapResult(GetResponse response, Class<T> clazz) {
|
||||
return null; //To change body of implemented methods use File | Settings | File Templates.
|
||||
return null; // To change body of implemented methods use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> clazz, Pageable pageable) {
|
||||
return null; //To change body of implemented methods use File | Settings | File Templates.
|
||||
return null; // To change body of implemented methods use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> LinkedList<T> mapResults(MultiGetResponse responses, Class<T> clazz) {
|
||||
public <T> List<T> mapResults(MultiGetResponse responses, Class<T> clazz) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
+66
-27
@@ -17,23 +17,31 @@ package org.springframework.data.elasticsearch.core;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Locale;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.annotation.ReadOnlyProperty;
|
||||
import org.springframework.data.annotation.Transient;
|
||||
import org.springframework.data.elasticsearch.annotations.Document;
|
||||
import org.springframework.data.elasticsearch.annotations.GeoPointField;
|
||||
import org.springframework.data.elasticsearch.core.geo.GeoPoint;
|
||||
import org.springframework.data.elasticsearch.core.mapping.SimpleElasticsearchMappingContext;
|
||||
import org.springframework.data.elasticsearch.entities.Car;
|
||||
import org.springframework.data.elasticsearch.entities.GeoEntity;
|
||||
import org.springframework.data.geo.Point;
|
||||
|
||||
/**
|
||||
* @author Artur Konczak
|
||||
* @author Mohsin Husen
|
||||
* @author Oliver Gierke
|
||||
* @author Peter-Josef Meisch
|
||||
*/
|
||||
public class DefaultEntityMapperTests {
|
||||
|
||||
@@ -49,62 +57,64 @@ public class DefaultEntityMapperTests {
|
||||
|
||||
@Test
|
||||
public void shouldMapObjectToJsonString() throws IOException {
|
||||
//Given
|
||||
|
||||
//When
|
||||
String jsonResult = entityMapper.mapToString(Car.builder().model(CAR_MODEL).name(CAR_NAME).build());
|
||||
// given
|
||||
|
||||
//Then
|
||||
// when
|
||||
String jsonResult = entityMapper.mapToString(new Car(CAR_NAME, CAR_MODEL));
|
||||
|
||||
// then
|
||||
assertThat(jsonResult).isEqualTo(JSON_STRING);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldMapJsonStringToObject() throws IOException {
|
||||
//Given
|
||||
|
||||
//When
|
||||
// given
|
||||
|
||||
// when
|
||||
Car result = entityMapper.mapToObject(JSON_STRING, Car.class);
|
||||
|
||||
//Then
|
||||
// then
|
||||
assertThat(result.getName()).isEqualTo(CAR_NAME);
|
||||
assertThat(result.getModel()).isEqualTo(CAR_MODEL);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldMapGeoPointElasticsearchNames() throws IOException {
|
||||
//given
|
||||
final Point point = new Point(10, 20);
|
||||
final String pointAsString = point.getX() + "," + point.getY();
|
||||
final double[] pointAsArray = {point.getX(), point.getY()};
|
||||
final GeoEntity geoEntity = GeoEntity.builder()
|
||||
.pointA(point).pointB(GeoPoint.fromPoint(point)).pointC(pointAsString).pointD(pointAsArray)
|
||||
.build();
|
||||
//when
|
||||
// given
|
||||
Point point = new Point(10, 20);
|
||||
String pointAsString = point.getX() + "," + point.getY();
|
||||
double[] pointAsArray = { point.getX(), point.getY() };
|
||||
GeoEntity geoEntity = GeoEntity.builder().pointA(point).pointB(GeoPoint.fromPoint(point)).pointC(pointAsString)
|
||||
.pointD(pointAsArray).build();
|
||||
// when
|
||||
String jsonResult = entityMapper.mapToString(geoEntity);
|
||||
|
||||
//then
|
||||
// then
|
||||
assertThat(jsonResult).contains(pointTemplate("pointA", point));
|
||||
assertThat(jsonResult).contains(pointTemplate("pointB", point));
|
||||
assertThat(jsonResult).contains(String.format(Locale.ENGLISH, "\"%s\":\"%s\"", "pointC", pointAsString));
|
||||
assertThat(jsonResult).contains(String.format(Locale.ENGLISH, "\"%s\":[%.1f,%.1f]", "pointD", pointAsArray[0], pointAsArray[1]));
|
||||
assertThat(jsonResult)
|
||||
.contains(String.format(Locale.ENGLISH, "\"%s\":[%.1f,%.1f]", "pointD", pointAsArray[0], pointAsArray[1]));
|
||||
}
|
||||
|
||||
|
||||
@Test // DATAES-464
|
||||
public void ignoresReadOnlyProperties() throws IOException {
|
||||
|
||||
|
||||
// given
|
||||
Sample sample = new Sample();
|
||||
sample.readOnly = "readOnly";
|
||||
sample.property = "property";
|
||||
sample.transientProperty = "transient";
|
||||
sample.annotatedTransientProperty = "transient";
|
||||
|
||||
|
||||
// when
|
||||
String result = entityMapper.mapToString(sample);
|
||||
|
||||
|
||||
// then
|
||||
assertThat(result).contains("\"property\"");
|
||||
|
||||
|
||||
assertThat(result).doesNotContain("readOnly");
|
||||
assertThat(result).doesNotContain("transientProperty");
|
||||
assertThat(result).doesNotContain("annotatedTransientProperty");
|
||||
@@ -113,13 +123,42 @@ public class DefaultEntityMapperTests {
|
||||
private String pointTemplate(String name, Point point) {
|
||||
return String.format(Locale.ENGLISH, "\"%s\":{\"lat\":%.1f,\"lon\":%.1f}", name, point.getX(), point.getY());
|
||||
}
|
||||
|
||||
|
||||
public static class Sample {
|
||||
|
||||
|
||||
|
||||
public @ReadOnlyProperty String readOnly;
|
||||
public @Transient String annotatedTransientProperty;
|
||||
public transient String transientProperty;
|
||||
public String property;
|
||||
}
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
static class Car {
|
||||
|
||||
private String name;
|
||||
private String model;
|
||||
}
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@Document(indexName = "test-index-geo-core-default-entity-mapper", type = "geo-test-index", shards = 1, replicas = 0,
|
||||
refreshInterval = "-1")
|
||||
static class GeoEntity {
|
||||
|
||||
@Id private String id;
|
||||
|
||||
// geo point - Custom implementation + Spring Data
|
||||
@GeoPointField private Point pointA;
|
||||
|
||||
private GeoPoint pointB;
|
||||
|
||||
@GeoPointField private String pointC;
|
||||
|
||||
@GeoPointField private double[] pointD;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+90
-48
@@ -16,17 +16,21 @@
|
||||
package org.springframework.data.elasticsearch.core;
|
||||
|
||||
import static java.util.Arrays.*;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
import static org.springframework.data.elasticsearch.annotations.FieldType.*;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.lang.Double;
|
||||
import java.lang.Long;
|
||||
import java.lang.Object;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.elasticsearch.action.get.GetResponse;
|
||||
@@ -47,14 +51,19 @@ import org.junit.runners.Parameterized;
|
||||
import org.junit.runners.Parameterized.Parameters;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.annotation.Version;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.elasticsearch.annotations.Document;
|
||||
import org.springframework.data.elasticsearch.annotations.Field;
|
||||
import org.springframework.data.elasticsearch.annotations.Score;
|
||||
import org.springframework.data.elasticsearch.annotations.ScriptedField;
|
||||
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
|
||||
import org.springframework.data.elasticsearch.core.geo.GeoPoint;
|
||||
import org.springframework.data.elasticsearch.core.mapping.SimpleElasticsearchMappingContext;
|
||||
import org.springframework.data.elasticsearch.entities.Car;
|
||||
import org.springframework.data.elasticsearch.entities.SampleEntity;
|
||||
|
||||
import com.fasterxml.jackson.databind.util.ArrayIterator;
|
||||
|
||||
@@ -65,6 +74,7 @@ import com.fasterxml.jackson.databind.util.ArrayIterator;
|
||||
* @author Mark Paluch
|
||||
* @author Ilkang Na
|
||||
* @author Christoph Strobl
|
||||
* @author Peter-Josef Meisch
|
||||
*/
|
||||
@RunWith(Parameterized.class)
|
||||
public class DefaultResultMapperTests {
|
||||
@@ -99,7 +109,8 @@ public class DefaultResultMapperTests {
|
||||
|
||||
@Test
|
||||
public void shouldMapAggregationsToPage() {
|
||||
// Given
|
||||
|
||||
// given
|
||||
SearchHit[] hits = { createCarHit("Ford", "Grat"), createCarHit("BMW", "Arrow") };
|
||||
SearchHits searchHits = mock(SearchHits.class);
|
||||
when(searchHits.getTotalHits()).thenReturn(2L);
|
||||
@@ -109,70 +120,70 @@ public class DefaultResultMapperTests {
|
||||
Aggregations aggregations = new Aggregations(asList(createCarAggregation()));
|
||||
when(response.getAggregations()).thenReturn(aggregations);
|
||||
|
||||
// When
|
||||
AggregatedPage<Car> page = (AggregatedPage<Car>) resultMapper.mapResults(response, Car.class, Pageable.unpaged());
|
||||
// when
|
||||
AggregatedPage<Car> page = resultMapper.mapResults(response, Car.class, Pageable.unpaged());
|
||||
|
||||
// Then
|
||||
// then
|
||||
page.hasFacets();
|
||||
assertThat(page.hasAggregations(), is(true));
|
||||
assertThat(page.getAggregation("Diesel").getName(), is("Diesel"));
|
||||
assertThat(page.hasAggregations()).isTrue();
|
||||
assertThat(page.getAggregation("Diesel").getName()).isEqualTo("Diesel");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldMapSearchRequestToPage() {
|
||||
// Given
|
||||
|
||||
// given
|
||||
SearchHit[] hits = { createCarHit("Ford", "Grat"), createCarHit("BMW", "Arrow") };
|
||||
SearchHits searchHits = mock(SearchHits.class);
|
||||
when(searchHits.getTotalHits()).thenReturn(2L);
|
||||
when(searchHits.iterator()).thenReturn(new ArrayIterator(hits));
|
||||
when(response.getHits()).thenReturn(searchHits);
|
||||
|
||||
// When
|
||||
// when
|
||||
Page<Car> page = resultMapper.mapResults(response, Car.class, Pageable.unpaged());
|
||||
|
||||
// Then
|
||||
assertThat(page.hasContent(), is(true));
|
||||
assertThat(page.getTotalElements(), is(2L));
|
||||
assertThat(page.getContent().get(0).getName(), is("Ford"));
|
||||
// then
|
||||
assertThat(page.hasContent()).isTrue();
|
||||
assertThat(page.getTotalElements()).isEqualTo(2);
|
||||
assertThat(page.getContent().get(0).getName()).isEqualTo("Ford");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldMapPartialSearchRequestToObject() {
|
||||
// Given
|
||||
|
||||
// given
|
||||
SearchHit[] hits = { createCarPartialHit("Ford", "Grat"), createCarPartialHit("BMW", "Arrow") };
|
||||
SearchHits searchHits = mock(SearchHits.class);
|
||||
when(searchHits.getTotalHits()).thenReturn(2L);
|
||||
when(searchHits.iterator()).thenReturn(new ArrayIterator(hits));
|
||||
when(response.getHits()).thenReturn(searchHits);
|
||||
|
||||
// When
|
||||
// when
|
||||
Page<Car> page = resultMapper.mapResults(response, Car.class, Pageable.unpaged());
|
||||
|
||||
// Then
|
||||
assertThat(page.hasContent(), is(true));
|
||||
assertThat(page.getTotalElements(), is(2L));
|
||||
assertThat(page.getContent().get(0).getName(), is("Ford"));
|
||||
// then
|
||||
assertThat(page.hasContent()).isTrue();
|
||||
assertThat(page.getTotalElements()).isEqualTo(2);
|
||||
assertThat(page.getContent().get(0).getName()).isEqualTo("Ford");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldMapGetRequestToObject() {
|
||||
// Given
|
||||
|
||||
// given
|
||||
GetResponse response = mock(GetResponse.class);
|
||||
when(response.getSourceAsString()).thenReturn(createJsonCar("Ford", "Grat"));
|
||||
|
||||
// When
|
||||
// when
|
||||
Car result = resultMapper.mapResult(response, Car.class);
|
||||
|
||||
// Then
|
||||
assertThat(result, notNullValue());
|
||||
assertThat(result.getModel(), is("Grat"));
|
||||
assertThat(result.getName(), is("Ford"));
|
||||
// then
|
||||
assertThat(result).isNotNull();
|
||||
assertThat(result.getModel()).isEqualTo("Grat");
|
||||
assertThat(result.getName()).isEqualTo("Ford");
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAES-281.
|
||||
*/
|
||||
@Test
|
||||
@Test // DATAES-281
|
||||
@Ignore("fix me - UnsupportedOperation")
|
||||
public void setsIdentifierOnImmutableType() {
|
||||
|
||||
@@ -182,24 +193,26 @@ public class DefaultResultMapperTests {
|
||||
|
||||
ImmutableEntity result = resultMapper.mapResult(response, ImmutableEntity.class);
|
||||
|
||||
assertThat(result, is(notNullValue()));
|
||||
assertThat(result.getId(), is("identifier"));
|
||||
assertThat(result).isNotNull();
|
||||
assertThat(result.getId()).isEqualTo("identifier");
|
||||
}
|
||||
|
||||
@Test // DATAES-198
|
||||
public void setsVersionFromGetResponse() {
|
||||
|
||||
GetResponse response = mock(GetResponse.class);
|
||||
when(response.getSourceAsString()).thenReturn("{}");
|
||||
when(response.getVersion()).thenReturn(1234L);
|
||||
|
||||
SampleEntity result = resultMapper.mapResult(response, SampleEntity.class);
|
||||
MappedEntity result = resultMapper.mapResult(response, MappedEntity.class);
|
||||
|
||||
assertThat(result, is(notNullValue()));
|
||||
assertThat(result.getVersion(), is(1234L));
|
||||
assertThat(result).isNotNull();
|
||||
assertThat(result.getVersion()).isEqualTo(1234);
|
||||
}
|
||||
|
||||
@Test // DATAES-198
|
||||
public void setsVersionFromMultiGetResponse() {
|
||||
|
||||
GetResponse response1 = mock(GetResponse.class);
|
||||
when(response1.getSourceAsString()).thenReturn("{}");
|
||||
when(response1.isExists()).thenReturn(true);
|
||||
@@ -214,17 +227,17 @@ public class DefaultResultMapperTests {
|
||||
when(multiResponse.getResponses()).thenReturn(new MultiGetItemResponse[] {
|
||||
new MultiGetItemResponse(response1, null), new MultiGetItemResponse(response2, null) });
|
||||
|
||||
LinkedList<SampleEntity> results = resultMapper.mapResults(multiResponse, SampleEntity.class);
|
||||
List<MappedEntity> results = resultMapper.mapResults(multiResponse, MappedEntity.class);
|
||||
|
||||
assertThat(results, is(notNullValue()));
|
||||
assertThat(results, hasSize(2));
|
||||
assertThat(results).isNotNull().hasSize(2);
|
||||
|
||||
assertThat(results.get(0).getVersion(), is(1234L));
|
||||
assertThat(results.get(1).getVersion(), is(5678L));
|
||||
assertThat(results.get(0).getVersion()).isEqualTo(1234);
|
||||
assertThat(results.get(1).getVersion()).isEqualTo(5678);
|
||||
}
|
||||
|
||||
@Test // DATAES-198
|
||||
public void setsVersionFromSearchResponse() {
|
||||
|
||||
SearchHit hit1 = mock(SearchHit.class);
|
||||
when(hit1.getSourceAsString()).thenReturn("{}");
|
||||
when(hit1.getVersion()).thenReturn(1234L);
|
||||
@@ -240,28 +253,31 @@ public class DefaultResultMapperTests {
|
||||
SearchResponse searchResponse = mock(SearchResponse.class);
|
||||
when(searchResponse.getHits()).thenReturn(searchHits);
|
||||
|
||||
AggregatedPage<SampleEntity> results = resultMapper.mapResults(searchResponse, SampleEntity.class,
|
||||
AggregatedPage<MappedEntity> results = resultMapper.mapResults(searchResponse, MappedEntity.class,
|
||||
mock(Pageable.class));
|
||||
|
||||
assertThat(results, is(notNullValue()));
|
||||
assertThat(results).isNotNull();
|
||||
|
||||
assertThat(results.getContent().get(0).getVersion(), is(1234L));
|
||||
assertThat(results.getContent().get(1).getVersion(), is(5678L));
|
||||
assertThat(results.getContent().get(0).getVersion()).isEqualTo(1234);
|
||||
assertThat(results.getContent().get(1).getVersion()).isEqualTo(5678);
|
||||
}
|
||||
|
||||
private Aggregation createCarAggregation() {
|
||||
|
||||
Aggregation aggregation = mock(Terms.class);
|
||||
when(aggregation.getName()).thenReturn("Diesel");
|
||||
return aggregation;
|
||||
}
|
||||
|
||||
private SearchHit createCarHit(String name, String model) {
|
||||
|
||||
SearchHit hit = mock(SearchHit.class);
|
||||
when(hit.getSourceAsString()).thenReturn(createJsonCar(name, model));
|
||||
return hit;
|
||||
}
|
||||
|
||||
private SearchHit createCarPartialHit(String name, String model) {
|
||||
|
||||
SearchHit hit = mock(SearchHit.class);
|
||||
when(hit.getSourceAsString()).thenReturn(null);
|
||||
when(hit.getFields()).thenReturn(createCarFields(name, model));
|
||||
@@ -269,7 +285,8 @@ public class DefaultResultMapperTests {
|
||||
}
|
||||
|
||||
private String createJsonCar(String name, String model) {
|
||||
final String q = "\"";
|
||||
|
||||
String q = "\"";
|
||||
StringBuffer sb = new StringBuffer();
|
||||
sb.append("{").append(q).append("name").append(q).append(":").append(q).append(name).append(q).append(",");
|
||||
sb.append(q).append("model").append(q).append(":").append(q).append(model).append(q).append("}");
|
||||
@@ -277,6 +294,7 @@ public class DefaultResultMapperTests {
|
||||
}
|
||||
|
||||
private Map<String, DocumentField> createCarFields(String name, String model) {
|
||||
|
||||
Map<String, DocumentField> result = new HashMap<>();
|
||||
result.put("name", new DocumentField("name", asList(name)));
|
||||
result.put("model", new DocumentField("model", asList(model)));
|
||||
@@ -290,4 +308,28 @@ public class DefaultResultMapperTests {
|
||||
|
||||
private final String id, name;
|
||||
}
|
||||
|
||||
@Data
|
||||
static class Car {
|
||||
|
||||
private String name;
|
||||
private String model;
|
||||
}
|
||||
|
||||
@Data
|
||||
@Document(indexName = "test-index-sample-default-result-mapper", type = "test-type")
|
||||
static class MappedEntity {
|
||||
|
||||
@Id private String id;
|
||||
@Field(type = Text, store = true, fielddata = true) private String type;
|
||||
@Field(type = Text, store = true, fielddata = true) private String message;
|
||||
private int rate;
|
||||
@ScriptedField private Double scriptedRate;
|
||||
private boolean available;
|
||||
private String highlightedMessage;
|
||||
private GeoPoint location;
|
||||
@Version private Long version;
|
||||
@Score private float score;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+51
-8
@@ -17,9 +17,12 @@ package org.springframework.data.elasticsearch.core;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.io.IOException;
|
||||
@@ -35,6 +38,7 @@ import java.util.Map;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.core.convert.support.GenericConversionService;
|
||||
import org.springframework.data.annotation.Id;
|
||||
@@ -43,19 +47,22 @@ import org.springframework.data.annotation.Transient;
|
||||
import org.springframework.data.annotation.TypeAlias;
|
||||
import org.springframework.data.convert.ReadingConverter;
|
||||
import org.springframework.data.convert.WritingConverter;
|
||||
import org.springframework.data.elasticsearch.annotations.Document;
|
||||
import org.springframework.data.elasticsearch.annotations.GeoPointField;
|
||||
import org.springframework.data.elasticsearch.core.convert.ElasticsearchCustomConversions;
|
||||
import org.springframework.data.elasticsearch.core.geo.GeoPoint;
|
||||
import org.springframework.data.elasticsearch.core.mapping.SimpleElasticsearchMappingContext;
|
||||
import org.springframework.data.elasticsearch.entities.Car;
|
||||
import org.springframework.data.elasticsearch.entities.GeoEntity;
|
||||
import org.springframework.data.geo.Box;
|
||||
import org.springframework.data.geo.Circle;
|
||||
import org.springframework.data.geo.Point;
|
||||
import org.springframework.data.geo.Polygon;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public class ElasticsearchEntityMapperUnitTests {
|
||||
|
||||
static final String JSON_STRING = "{\"_class\":\"org.springframework.data.elasticsearch.entities.Car\",\"name\":\"Grat\",\"model\":\"Ford\"}";
|
||||
static final String JSON_STRING = "{\"_class\":\"org.springframework.data.elasticsearch.core.ElasticsearchEntityMapperUnitTests$Car\",\"name\":\"Grat\",\"model\":\"Ford\"}";
|
||||
static final String CAR_MODEL = "Ford";
|
||||
static final String CAR_NAME = "Grat";
|
||||
ElasticsearchEntityMapper entityMapper;
|
||||
@@ -203,11 +210,11 @@ public class ElasticsearchEntityMapperUnitTests {
|
||||
@Test // DATAES-530
|
||||
public void shouldMapGeoPointElasticsearchNames() throws IOException {
|
||||
// given
|
||||
final Point point = new Point(10, 20);
|
||||
final String pointAsString = point.getX() + "," + point.getY();
|
||||
final double[] pointAsArray = { point.getX(), point.getY() };
|
||||
final GeoEntity geoEntity = GeoEntity.builder().pointA(point).pointB(GeoPoint.fromPoint(point))
|
||||
.pointC(pointAsString).pointD(pointAsArray).build();
|
||||
Point point = new Point(10, 20);
|
||||
String pointAsString = point.getX() + "," + point.getY();
|
||||
double[] pointAsArray = { point.getX(), point.getY() };
|
||||
GeoEntity geoEntity = GeoEntity.builder().pointA(point).pointB(GeoPoint.fromPoint(point)).pointC(pointAsString)
|
||||
.pointD(pointAsArray).build();
|
||||
// when
|
||||
String jsonResult = entityMapper.mapToString(geoEntity);
|
||||
|
||||
@@ -674,4 +681,40 @@ public class ElasticsearchEntityMapperUnitTests {
|
||||
return new ShotGun(source.get("model").toString());
|
||||
}
|
||||
}
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
static class Car {
|
||||
|
||||
private String name;
|
||||
private String model;
|
||||
}
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@Document(indexName = "test-index-geo-core-entity-mapper", type = "geo-test-index", shards = 1, replicas = 0,
|
||||
refreshInterval = "-1")
|
||||
static class GeoEntity {
|
||||
|
||||
@Id private String id;
|
||||
|
||||
// geo shape - Spring Data
|
||||
private Box box;
|
||||
private Circle circle;
|
||||
private Polygon polygon;
|
||||
|
||||
// geo point - Custom implementation + Spring Data
|
||||
@GeoPointField private Point pointA;
|
||||
|
||||
private GeoPoint pointB;
|
||||
|
||||
@GeoPointField private String pointC;
|
||||
|
||||
@GeoPointField private double[] pointD;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+55
-3
@@ -16,17 +16,30 @@
|
||||
package org.springframework.data.elasticsearch.core;
|
||||
|
||||
import static org.apache.commons.lang.RandomStringUtils.*;
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.springframework.data.elasticsearch.annotations.FieldType.*;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.elasticsearch.ElasticsearchStatusException;
|
||||
import org.elasticsearch.action.index.IndexRequest;
|
||||
import org.elasticsearch.action.update.UpdateRequest;
|
||||
import org.elasticsearch.common.xcontent.XContentType;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.elasticsearch.annotations.Document;
|
||||
import org.springframework.data.elasticsearch.annotations.Field;
|
||||
import org.springframework.data.elasticsearch.core.query.UpdateQuery;
|
||||
import org.springframework.data.elasticsearch.core.query.UpdateQueryBuilder;
|
||||
import org.springframework.data.elasticsearch.entities.SampleEntity;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
/**
|
||||
* @author Rizwan Idrees
|
||||
@@ -40,13 +53,15 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
* @author Alen Turkovic
|
||||
* @author Sascha Woo
|
||||
* @author Don Wellington
|
||||
* @author Peter-Josef Meisch
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@RunWith(SpringRunner.class)
|
||||
@ContextConfiguration("classpath:elasticsearch-rest-template-test.xml")
|
||||
public class ElasticsearchRestTemplateTests extends ElasticsearchTemplateTests {
|
||||
|
||||
@Test(expected = ElasticsearchStatusException.class)
|
||||
public void shouldThrowExceptionIfDocumentDoesNotExistWhileDoingPartialUpdate() {
|
||||
|
||||
// when
|
||||
IndexRequest indexRequest = new IndexRequest();
|
||||
indexRequest.source("{}", XContentType.JSON);
|
||||
@@ -54,4 +69,41 @@ public class ElasticsearchRestTemplateTests extends ElasticsearchTemplateTests {
|
||||
.withIndexRequest(indexRequest).build();
|
||||
elasticsearchTemplate.update(updateQuery);
|
||||
}
|
||||
|
||||
@Test // DATAES-227
|
||||
@Override
|
||||
public void shouldUseUpsertOnUpdate() throws IOException {
|
||||
|
||||
// given
|
||||
Map<String, Object> doc = new HashMap<>();
|
||||
doc.put("id", "1");
|
||||
doc.put("message", "test");
|
||||
|
||||
UpdateRequest updateRequest = new UpdateRequest() //
|
||||
.doc(doc) //
|
||||
.upsert(doc);
|
||||
|
||||
UpdateQuery updateQuery = new UpdateQueryBuilder() //
|
||||
.withClass(SampleEntity.class) //
|
||||
.withId("1") //
|
||||
.withUpdateRequest(updateRequest).build();
|
||||
|
||||
// when
|
||||
UpdateRequest request = (UpdateRequest) ReflectionTestUtils //
|
||||
.invokeMethod(elasticsearchTemplate, "prepareUpdate", updateQuery);
|
||||
|
||||
// then
|
||||
assertThat(request).isNotNull();
|
||||
assertThat(request.upsertRequest()).isNotNull();
|
||||
}
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@Document(indexName = "test-index-sample-core-rest-template", type = "test-type", shards = 1, replicas = 0,
|
||||
refreshInterval = "-1")
|
||||
static class SampleEntity {
|
||||
|
||||
@Id private String id;
|
||||
@Field(type = Text, store = true, fielddata = true) private String type;
|
||||
}
|
||||
}
|
||||
|
||||
+15
-15
@@ -15,37 +15,37 @@
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.core;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
/**
|
||||
* @author Artur Konczak
|
||||
* @author Peter-Josef Meisch
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@RunWith(SpringRunner.class)
|
||||
@ContextConfiguration("classpath:elasticsearch-template-custom-mapper.xml")
|
||||
public class ElasticsearchTemplateCustomMapperTests {
|
||||
|
||||
@Autowired
|
||||
private ElasticsearchTemplate elasticsearchTemplate;
|
||||
@Autowired private ElasticsearchTemplate elasticsearchTemplate;
|
||||
|
||||
@Autowired
|
||||
private EntityMapper entityMapper;
|
||||
@Autowired private EntityMapper entityMapper;
|
||||
|
||||
@Autowired
|
||||
private ResultsMapper resultsMapper;
|
||||
@Autowired private ResultsMapper resultsMapper;
|
||||
|
||||
@Test
|
||||
public void shouldUseCustomMapper() {
|
||||
//given
|
||||
//when
|
||||
//them
|
||||
assertThat(elasticsearchTemplate.getResultsMapper(), is(resultsMapper));
|
||||
assertThat(elasticsearchTemplate.getResultsMapper().getEntityMapper(), is(entityMapper));
|
||||
|
||||
// given
|
||||
|
||||
// when
|
||||
|
||||
// then
|
||||
assertThat(elasticsearchTemplate.getResultsMapper()).isSameAs(resultsMapper);
|
||||
assertThat(elasticsearchTemplate.getResultsMapper().getEntityMapper()).isSameAs(entityMapper);
|
||||
}
|
||||
}
|
||||
|
||||
+103
-26
@@ -15,10 +15,9 @@
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.core;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.elasticsearch.common.xcontent.XContentFactory.*;
|
||||
import static org.elasticsearch.join.query.JoinQueryBuilders.hasChildQuery;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.elasticsearch.join.query.JoinQueryBuilders.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -29,48 +28,46 @@ import org.elasticsearch.action.update.UpdateResponse;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.index.query.QueryBuilder;
|
||||
import org.elasticsearch.index.query.QueryBuilders;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.style.ToStringCreator;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.elasticsearch.annotations.Document;
|
||||
import org.springframework.data.elasticsearch.annotations.Field;
|
||||
import org.springframework.data.elasticsearch.annotations.FieldType;
|
||||
import org.springframework.data.elasticsearch.annotations.Parent;
|
||||
import org.springframework.data.elasticsearch.core.ElasticsearchTemplateParentChildTests.ParentEntity.ChildEntity;
|
||||
import org.springframework.data.elasticsearch.core.query.IndexQuery;
|
||||
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
|
||||
import org.springframework.data.elasticsearch.core.query.UpdateQuery;
|
||||
import org.springframework.data.elasticsearch.entities.ParentEntity;
|
||||
import org.springframework.data.elasticsearch.entities.ParentEntity.ChildEntity;
|
||||
import org.springframework.data.elasticsearch.utils.IndexInitializer;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
/**
|
||||
* @author Philipp Jardas
|
||||
* @author Peter-Josef Meisch
|
||||
*/
|
||||
@Ignore(value = "DATAES-421")
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@RunWith(SpringRunner.class)
|
||||
@ContextConfiguration("classpath:elasticsearch-template-test.xml")
|
||||
public class ElasticsearchTemplateParentChildTests {
|
||||
|
||||
@Autowired
|
||||
private ElasticsearchTemplate elasticsearchTemplate;
|
||||
@Autowired private ElasticsearchTemplate elasticsearchTemplate;
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
clean();
|
||||
elasticsearchTemplate.createIndex(ParentEntity.class);
|
||||
elasticsearchTemplate.createIndex(ChildEntity.class);
|
||||
elasticsearchTemplate.putMapping(ChildEntity.class);
|
||||
}
|
||||
|
||||
@After
|
||||
public void clean() {
|
||||
elasticsearchTemplate.deleteIndex(ChildEntity.class);
|
||||
elasticsearchTemplate.deleteIndex(ParentEntity.class);
|
||||
IndexInitializer.init(elasticsearchTemplate, ParentEntity.class);
|
||||
IndexInitializer.init(elasticsearchTemplate, ChildEntity.class);
|
||||
}
|
||||
|
||||
@Ignore(value = "DATAES-421")
|
||||
@Test
|
||||
public void shouldIndexParentChildEntity() {
|
||||
|
||||
// index two parents
|
||||
ParentEntity parent1 = index("parent1", "First Parent");
|
||||
ParentEntity parent2 = index("parent2", "Second Parent");
|
||||
@@ -84,16 +81,19 @@ public class ElasticsearchTemplateParentChildTests {
|
||||
elasticsearchTemplate.refresh(ChildEntity.class);
|
||||
|
||||
// find all parents that have the first child
|
||||
QueryBuilder query = hasChildQuery(ParentEntity.CHILD_TYPE, QueryBuilders.termQuery("name", child1name.toLowerCase()), ScoreMode.None);
|
||||
QueryBuilder query = hasChildQuery(ParentEntity.CHILD_TYPE,
|
||||
QueryBuilders.termQuery("name", child1name.toLowerCase()), ScoreMode.None);
|
||||
List<ParentEntity> parents = elasticsearchTemplate.queryForList(new NativeSearchQuery(query), ParentEntity.class);
|
||||
|
||||
// we're expecting only the first parent as result
|
||||
assertThat("parents", parents, contains(hasProperty("id", is(parent1.getId()))));
|
||||
assertThat(parents).hasSize(1);
|
||||
assertThat(parents.get(0).getId()).isEqualTo(parent1.getId());
|
||||
}
|
||||
|
||||
@Ignore(value = "DATAES-421")
|
||||
@Test
|
||||
public void shouldUpdateChild() throws Exception {
|
||||
|
||||
// index parent and child
|
||||
ParentEntity parent = index("parent", "Parent");
|
||||
ChildEntity child = index("child", parent.getId(), "Child");
|
||||
@@ -103,16 +103,17 @@ public class ElasticsearchTemplateParentChildTests {
|
||||
UpdateRequest updateRequest = new UpdateRequest(ParentEntity.INDEX, ParentEntity.CHILD_TYPE, child.getId());
|
||||
updateRequest.routing(parent.getId());
|
||||
XContentBuilder builder;
|
||||
builder = jsonBuilder().startObject().field("name", newChildName).endObject();
|
||||
builder = jsonBuilder().startObject().field("name", newChildName).endObject();
|
||||
updateRequest.doc(builder);
|
||||
final UpdateResponse response = update(updateRequest);
|
||||
UpdateResponse response = update(updateRequest);
|
||||
|
||||
assertThat(response.getShardInfo().getSuccessful(), is(1));
|
||||
assertThat(response.getShardInfo().getSuccessful()).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Ignore(value = "DATAES-421")
|
||||
@Test(expected = RoutingMissingException.class)
|
||||
public void shouldFailWithRoutingMissingExceptionOnUpdateChildIfNotRoutingSetOnUpdateRequest() throws Exception {
|
||||
|
||||
// index parent and child
|
||||
ParentEntity parent = index("parent", "Parent");
|
||||
ChildEntity child = index("child", parent.getId(), "Child");
|
||||
@@ -129,6 +130,7 @@ public class ElasticsearchTemplateParentChildTests {
|
||||
@Ignore(value = "DATAES-421")
|
||||
@Test(expected = RoutingMissingException.class)
|
||||
public void shouldFailWithRoutingMissingExceptionOnUpdateChildIfRoutingOnlySetOnRequestDoc() throws Exception {
|
||||
|
||||
// index parent and child
|
||||
ParentEntity parent = index("parent", "Parent");
|
||||
ChildEntity child = index("child", parent.getId(), "Child");
|
||||
@@ -144,6 +146,7 @@ public class ElasticsearchTemplateParentChildTests {
|
||||
}
|
||||
|
||||
private ParentEntity index(String parentId, String name) {
|
||||
|
||||
ParentEntity parent = new ParentEntity(parentId, name);
|
||||
IndexQuery index = new IndexQuery();
|
||||
index.setId(parent.getId());
|
||||
@@ -154,6 +157,7 @@ public class ElasticsearchTemplateParentChildTests {
|
||||
}
|
||||
|
||||
private ChildEntity index(String childId, String parentId, String name) {
|
||||
|
||||
ChildEntity child = new ChildEntity(childId, parentId, name);
|
||||
IndexQuery index = new IndexQuery();
|
||||
index.setId(child.getId());
|
||||
@@ -165,11 +169,84 @@ public class ElasticsearchTemplateParentChildTests {
|
||||
}
|
||||
|
||||
private UpdateResponse update(UpdateRequest updateRequest) {
|
||||
final UpdateQuery update = new UpdateQuery();
|
||||
|
||||
UpdateQuery update = new UpdateQuery();
|
||||
update.setId(updateRequest.id());
|
||||
update.setType(updateRequest.type());
|
||||
update.setIndexName(updateRequest.index());
|
||||
update.setUpdateRequest(updateRequest);
|
||||
return elasticsearchTemplate.update(update);
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Philipp Jardas
|
||||
* @author Mohsin Husen
|
||||
*/
|
||||
@Document(
|
||||
indexName = org.springframework.data.elasticsearch.core.ElasticsearchTemplateParentChildTests.ParentEntity.INDEX,
|
||||
type = org.springframework.data.elasticsearch.core.ElasticsearchTemplateParentChildTests.ParentEntity.PARENT_TYPE,
|
||||
shards = 1, replicas = 0, refreshInterval = "-1")
|
||||
static class ParentEntity {
|
||||
|
||||
public static final String INDEX = "parent-child";
|
||||
public static final String PARENT_TYPE = "parent-entity";
|
||||
public static final String CHILD_TYPE = "child-entity";
|
||||
|
||||
@Id private String id;
|
||||
@Field(type = FieldType.Text, store = true) private String name;
|
||||
|
||||
public ParentEntity() {}
|
||||
|
||||
public ParentEntity(String id, String name) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringCreator(this).append("id", id).append("name", name).toString();
|
||||
}
|
||||
|
||||
@Document(indexName = INDEX, type = CHILD_TYPE, shards = 1, replicas = 0, refreshInterval = "-1")
|
||||
static class ChildEntity {
|
||||
|
||||
@Id private String id;
|
||||
@Field(type = FieldType.Text, store = true) @Parent(type = PARENT_TYPE) private String parentId;
|
||||
@Field(type = FieldType.Text, store = true) private String name;
|
||||
|
||||
public ChildEntity() {}
|
||||
|
||||
public ChildEntity(String id, String parentId, String name) {
|
||||
this.id = id;
|
||||
this.parentId = parentId;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getParentId() {
|
||||
return parentId;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringCreator(this).append("id", id).append("parentId", parentId).append("name", name).toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1010
-351
File diff suppressed because it is too large
Load Diff
+38
-5
@@ -1,19 +1,43 @@
|
||||
/*
|
||||
* Copyright 2018-2019 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.apache.commons.lang.RandomStringUtils.*;
|
||||
import static org.springframework.data.elasticsearch.annotations.FieldType.*;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import org.elasticsearch.action.index.IndexRequest;
|
||||
import org.elasticsearch.common.xcontent.XContentType;
|
||||
import org.elasticsearch.index.engine.DocumentMissingException;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.elasticsearch.annotations.Document;
|
||||
import org.springframework.data.elasticsearch.annotations.Field;
|
||||
import org.springframework.data.elasticsearch.core.query.UpdateQuery;
|
||||
import org.springframework.data.elasticsearch.core.query.UpdateQueryBuilder;
|
||||
import org.springframework.data.elasticsearch.entities.SampleEntity;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
/**
|
||||
* @author Peter-Josef Meisch
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@ContextConfiguration("classpath:elasticsearch-template-test.xml")
|
||||
public class ElasticsearchTransportTemplateTests extends ElasticsearchTemplateTests {
|
||||
|
||||
@@ -22,8 +46,17 @@ public class ElasticsearchTransportTemplateTests extends ElasticsearchTemplateTe
|
||||
// when
|
||||
IndexRequest indexRequest = new IndexRequest();
|
||||
indexRequest.source("{}", XContentType.JSON);
|
||||
UpdateQuery updateQuery = new UpdateQueryBuilder().withId(randomNumeric(5))
|
||||
.withClass(SampleEntity.class).withIndexRequest(indexRequest).build();
|
||||
UpdateQuery updateQuery = new UpdateQueryBuilder().withId(randomNumeric(5)).withClass(SampleEntity.class)
|
||||
.withIndexRequest(indexRequest).build();
|
||||
elasticsearchTemplate.update(updateQuery);
|
||||
}
|
||||
|
||||
@Data
|
||||
@Document(indexName = "test-index-sample-core-transport-template", type = "test-type", shards = 1, replicas = 0,
|
||||
refreshInterval = "-1")
|
||||
static class SampleEntity {
|
||||
|
||||
@Id private String id;
|
||||
@Field(type = Text, store = true, fielddata = true) private String type;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,107 +15,173 @@
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.core;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.elasticsearch.index.query.QueryBuilders.*;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.hamcrest.core.Is.is;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.springframework.data.elasticsearch.annotations.FieldType.*;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import org.elasticsearch.action.search.SearchPhaseExecutionException;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.elasticsearch.core.facet.LogEntity;
|
||||
import org.springframework.data.elasticsearch.core.facet.LogEntityBuilder;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.elasticsearch.annotations.Document;
|
||||
import org.springframework.data.elasticsearch.annotations.Field;
|
||||
import org.springframework.data.elasticsearch.core.query.IndexQuery;
|
||||
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
|
||||
import org.springframework.data.elasticsearch.core.query.SearchQuery;
|
||||
import org.springframework.data.elasticsearch.utils.IndexInitializer;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
/**
|
||||
* LogEntityTests
|
||||
*
|
||||
* @author Artur Konczak
|
||||
* @author Mohsin Husen
|
||||
* @author Peter-Josef Meisch
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@RunWith(SpringRunner.class)
|
||||
@ContextConfiguration("classpath:elasticsearch-template-test.xml")
|
||||
public class LogEntityTests {
|
||||
|
||||
@Autowired
|
||||
private ElasticsearchTemplate template;
|
||||
@Autowired private ElasticsearchTemplate template;
|
||||
|
||||
@Before
|
||||
public void before() throws ParseException {
|
||||
template.deleteIndex(LogEntity.class);
|
||||
template.createIndex(LogEntity.class);
|
||||
template.putMapping(LogEntity.class);
|
||||
|
||||
IndexInitializer.init(template, LogEntity.class);
|
||||
|
||||
SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm");
|
||||
IndexQuery indexQuery1 = new LogEntityBuilder("1").action("update").date(dateFormatter.parse("2013-10-18 18:01")).code(2)
|
||||
.ip("10.10.10.1").buildIndex();
|
||||
IndexQuery indexQuery1 = new LogEntityBuilder("1").action("update").date(dateFormatter.parse("2013-10-18 18:01"))
|
||||
.code(2).ip("10.10.10.1").buildIndex();
|
||||
|
||||
IndexQuery indexQuery2 = new LogEntityBuilder("2").action("update").date(dateFormatter.parse("2013-10-19 18:02")).code(2)
|
||||
.ip("10.10.10.2").buildIndex();
|
||||
IndexQuery indexQuery2 = new LogEntityBuilder("2").action("update").date(dateFormatter.parse("2013-10-19 18:02"))
|
||||
.code(2).ip("10.10.10.2").buildIndex();
|
||||
|
||||
IndexQuery indexQuery3 = new LogEntityBuilder("3").action("update").date(dateFormatter.parse("2013-10-19 18:03")).code(2)
|
||||
.ip("10.10.10.3").buildIndex();
|
||||
IndexQuery indexQuery3 = new LogEntityBuilder("3").action("update").date(dateFormatter.parse("2013-10-19 18:03"))
|
||||
.code(2).ip("10.10.10.3").buildIndex();
|
||||
|
||||
IndexQuery indexQuery4 = new LogEntityBuilder("4").action("update").date(dateFormatter.parse("2013-10-19 18:04")).code(2)
|
||||
.ip("10.10.10.4").buildIndex();
|
||||
IndexQuery indexQuery4 = new LogEntityBuilder("4").action("update").date(dateFormatter.parse("2013-10-19 18:04"))
|
||||
.code(2).ip("10.10.10.4").buildIndex();
|
||||
|
||||
template.bulkIndex(Arrays.asList(indexQuery1, indexQuery2, indexQuery3, indexQuery4));
|
||||
template.refresh(LogEntity.class);
|
||||
}
|
||||
|
||||
/*
|
||||
DATAES-66
|
||||
*/
|
||||
@Test
|
||||
public void shouldIndexGivenLogEntityWithIPFieldType() throws ParseException {
|
||||
//when
|
||||
SearchQuery searchQuery = new NativeSearchQueryBuilder()
|
||||
.withQuery(termQuery("ip", "10.10.10.1")).build();
|
||||
@Test // DATAES-66
|
||||
public void shouldIndexGivenLogEntityWithIPFieldType() {
|
||||
|
||||
// when
|
||||
SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(termQuery("ip", "10.10.10.1")).build();
|
||||
List<LogEntity> entities = template.queryForList(searchQuery, LogEntity.class);
|
||||
//then
|
||||
assertThat(entities, is(notNullValue()));
|
||||
assertThat(entities.size(), is(1));
|
||||
|
||||
// then
|
||||
assertThat(entities).isNotNull().hasSize(1);
|
||||
}
|
||||
|
||||
/*
|
||||
DATAES-66
|
||||
*/
|
||||
@Test(expected = SearchPhaseExecutionException.class)
|
||||
@Test(expected = SearchPhaseExecutionException.class) // DATAES-66
|
||||
public void shouldThrowExceptionWhenInvalidIPGivenForSearchQuery() {
|
||||
//when
|
||||
SearchQuery searchQuery = new NativeSearchQueryBuilder()
|
||||
.withQuery(termQuery("ip", "10.10.10")).build();
|
||||
|
||||
// when
|
||||
SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(termQuery("ip", "10.10.10")).build();
|
||||
List<LogEntity> entities = template.queryForList(searchQuery, LogEntity.class);
|
||||
//then
|
||||
assertThat(entities, is(notNullValue()));
|
||||
assertThat(entities.size(), is(1));
|
||||
|
||||
// then
|
||||
assertThat(entities).isNotNull().hasSize(1);
|
||||
}
|
||||
|
||||
/*
|
||||
DATAES-66
|
||||
*/
|
||||
@Test
|
||||
@Test // DATAES-66
|
||||
public void shouldReturnLogsForGivenIPRanges() {
|
||||
//when
|
||||
|
||||
// when
|
||||
SearchQuery searchQuery = new NativeSearchQueryBuilder()
|
||||
.withQuery(rangeQuery("ip").from("10.10.10.1").to("10.10.10.3")).build();
|
||||
|
||||
List<LogEntity> entities = template.queryForList(searchQuery, LogEntity.class);
|
||||
//then
|
||||
assertThat(entities, is(notNullValue()));
|
||||
assertThat(entities.size(), is(3));
|
||||
|
||||
// then
|
||||
assertThat(entities).isNotNull().hasSize(3);
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple type to test facets
|
||||
*/
|
||||
@Data
|
||||
@Document(indexName = "test-index-log-core", type = "test-log-type", shards = 1, replicas = 0, refreshInterval = "-1")
|
||||
static class LogEntity {
|
||||
|
||||
private static final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
|
||||
|
||||
@Id private String id;
|
||||
|
||||
private String action;
|
||||
|
||||
private long sequenceCode;
|
||||
|
||||
@Field(type = Ip) private String ip;
|
||||
|
||||
@Field(type = Date) private java.util.Date date;
|
||||
|
||||
private LogEntity() {}
|
||||
|
||||
public LogEntity(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple type to test facets
|
||||
*
|
||||
* @author Artur Konczak
|
||||
* @author Mohsin Husen
|
||||
*/
|
||||
static class LogEntityBuilder {
|
||||
|
||||
private LogEntity result;
|
||||
|
||||
public LogEntityBuilder(String id) {
|
||||
result = new LogEntity(id);
|
||||
}
|
||||
|
||||
public LogEntityBuilder action(String action) {
|
||||
result.setAction(action);
|
||||
return this;
|
||||
}
|
||||
|
||||
public LogEntityBuilder code(long sequenceCode) {
|
||||
result.setSequenceCode(sequenceCode);
|
||||
return this;
|
||||
}
|
||||
|
||||
public LogEntityBuilder date(Date date) {
|
||||
result.setDate(date);
|
||||
return this;
|
||||
}
|
||||
|
||||
public LogEntityBuilder ip(String ip) {
|
||||
result.setIp(ip);
|
||||
return this;
|
||||
}
|
||||
|
||||
public LogEntity build() {
|
||||
return result;
|
||||
}
|
||||
|
||||
public IndexQuery buildIndex() {
|
||||
IndexQuery indexQuery = new IndexQuery();
|
||||
indexQuery.setId(result.getId());
|
||||
indexQuery.setObject(result);
|
||||
return indexQuery;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+372
-45
@@ -16,50 +16,60 @@
|
||||
|
||||
package org.springframework.data.elasticsearch.core;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.elasticsearch.index.query.QueryBuilders.*;
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.skyscreamer.jsonassert.JSONAssert.assertEquals;
|
||||
import static org.skyscreamer.jsonassert.JSONAssert.*;
|
||||
import static org.springframework.data.elasticsearch.annotations.FieldType.*;
|
||||
import static org.springframework.data.elasticsearch.utils.IndexBuilder.*;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.Boolean;
|
||||
import java.lang.Integer;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.assertj.core.data.Percentage;
|
||||
import org.json.JSONException;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.annotation.Transient;
|
||||
import org.springframework.data.elasticsearch.annotations.CompletionField;
|
||||
import org.springframework.data.elasticsearch.annotations.Document;
|
||||
import org.springframework.data.elasticsearch.annotations.Field;
|
||||
import org.springframework.data.elasticsearch.annotations.FieldType;
|
||||
import org.springframework.data.elasticsearch.annotations.GeoPointField;
|
||||
import org.springframework.data.elasticsearch.annotations.InnerField;
|
||||
import org.springframework.data.elasticsearch.annotations.Mapping;
|
||||
import org.springframework.data.elasticsearch.annotations.MultiField;
|
||||
import org.springframework.data.elasticsearch.annotations.Parent;
|
||||
import org.springframework.data.elasticsearch.builder.SampleInheritedEntityBuilder;
|
||||
import org.springframework.data.elasticsearch.annotations.Setting;
|
||||
import org.springframework.data.elasticsearch.core.completion.Completion;
|
||||
import org.springframework.data.elasticsearch.core.geo.GeoPoint;
|
||||
import org.springframework.data.elasticsearch.core.query.IndexQuery;
|
||||
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
|
||||
import org.springframework.data.elasticsearch.core.query.SearchQuery;
|
||||
import org.springframework.data.elasticsearch.entities.Book;
|
||||
import org.springframework.data.elasticsearch.entities.CopyToEntity;
|
||||
import org.springframework.data.elasticsearch.entities.GeoEntity;
|
||||
import org.springframework.data.elasticsearch.entities.Group;
|
||||
import org.springframework.data.elasticsearch.entities.NormalizerEntity;
|
||||
import org.springframework.data.elasticsearch.entities.SampleInheritedEntity;
|
||||
import org.springframework.data.elasticsearch.entities.SampleTransientEntity;
|
||||
import org.springframework.data.elasticsearch.entities.SimpleRecursiveEntity;
|
||||
import org.springframework.data.elasticsearch.entities.StockPrice;
|
||||
import org.springframework.data.elasticsearch.entities.User;
|
||||
import org.springframework.data.geo.Box;
|
||||
import org.springframework.data.geo.Circle;
|
||||
import org.springframework.data.geo.Point;
|
||||
import org.springframework.data.geo.Polygon;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
/**
|
||||
* @author Stuart Stevenson
|
||||
@@ -71,15 +81,29 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
* @author Sascha Woo
|
||||
* @author Peter-Josef Meisch
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@RunWith(SpringRunner.class)
|
||||
@ContextConfiguration("classpath:elasticsearch-template-test.xml")
|
||||
public class MappingBuilderTests extends MappingContextBaseTests {
|
||||
|
||||
@Autowired private ElasticsearchTemplate elasticsearchTemplate;
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
|
||||
elasticsearchTemplate.deleteIndex(StockPrice.class);
|
||||
elasticsearchTemplate.deleteIndex(SimpleRecursiveEntity.class);
|
||||
elasticsearchTemplate.deleteIndex(StockPrice.class);
|
||||
elasticsearchTemplate.deleteIndex(SampleInheritedEntity.class);
|
||||
elasticsearchTemplate.deleteIndex(User.class);
|
||||
elasticsearchTemplate.deleteIndex(Group.class);
|
||||
elasticsearchTemplate.deleteIndex(Book.class);
|
||||
elasticsearchTemplate.deleteIndex(NormalizerEntity.class);
|
||||
elasticsearchTemplate.deleteIndex(CopyToEntity.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldNotFailOnCircularReference() {
|
||||
elasticsearchTemplate.deleteIndex(SimpleRecursiveEntity.class);
|
||||
|
||||
elasticsearchTemplate.createIndex(SimpleRecursiveEntity.class);
|
||||
elasticsearchTemplate.putMapping(SimpleRecursiveEntity.class);
|
||||
elasticsearchTemplate.refresh(SimpleRecursiveEntity.class);
|
||||
@@ -115,7 +139,6 @@ public class MappingBuilderTests extends MappingContextBaseTests {
|
||||
// Given
|
||||
|
||||
// When
|
||||
elasticsearchTemplate.deleteIndex(StockPrice.class);
|
||||
elasticsearchTemplate.createIndex(StockPrice.class);
|
||||
elasticsearchTemplate.putMapping(StockPrice.class);
|
||||
String symbol = "AU";
|
||||
@@ -128,11 +151,12 @@ public class MappingBuilderTests extends MappingContextBaseTests {
|
||||
|
||||
SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchAllQuery()).build();
|
||||
List<StockPrice> result = elasticsearchTemplate.queryForList(searchQuery, StockPrice.class);
|
||||
|
||||
// Then
|
||||
assertThat(result.size(), is(1));
|
||||
assertThat(result).hasSize(1);
|
||||
StockPrice entry = result.get(0);
|
||||
assertThat(entry.getSymbol(), is(symbol));
|
||||
assertThat(entry.getPrice(), is(BigDecimal.valueOf(price)));
|
||||
assertThat(entry.getSymbol()).isEqualTo(symbol);
|
||||
assertThat(entry.getPrice()).isCloseTo(BigDecimal.valueOf(price), Percentage.withPercentage(0.01));
|
||||
}
|
||||
|
||||
@Test // DATAES-568
|
||||
@@ -160,10 +184,9 @@ public class MappingBuilderTests extends MappingContextBaseTests {
|
||||
@Test // DATAES-76
|
||||
public void shouldAddSampleInheritedEntityDocumentToIndex() {
|
||||
|
||||
// Given
|
||||
// given
|
||||
|
||||
// When
|
||||
elasticsearchTemplate.deleteIndex(SampleInheritedEntity.class);
|
||||
// when
|
||||
elasticsearchTemplate.createIndex(SampleInheritedEntity.class);
|
||||
elasticsearchTemplate.putMapping(SampleInheritedEntity.class);
|
||||
Date createdDate = new Date();
|
||||
@@ -175,12 +198,13 @@ public class MappingBuilderTests extends MappingContextBaseTests {
|
||||
|
||||
SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchAllQuery()).build();
|
||||
List<SampleInheritedEntity> result = elasticsearchTemplate.queryForList(searchQuery, SampleInheritedEntity.class);
|
||||
// Then
|
||||
assertThat(result.size(), is(1));
|
||||
|
||||
// then
|
||||
assertThat(result).hasSize(1);
|
||||
|
||||
SampleInheritedEntity entry = result.get(0);
|
||||
assertThat(entry.getCreatedDate(), is(createdDate));
|
||||
assertThat(entry.getMessage(), is(message));
|
||||
assertThat(entry.getCreatedDate()).isEqualTo(createdDate);
|
||||
assertThat(entry.getMessage()).isEqualTo(message);
|
||||
}
|
||||
|
||||
@Test // DATAES-568
|
||||
@@ -207,6 +231,7 @@ public class MappingBuilderTests extends MappingContextBaseTests {
|
||||
elasticsearchTemplate.putMapping(User.class);
|
||||
elasticsearchTemplate.createIndex(Group.class);
|
||||
elasticsearchTemplate.putMapping(Group.class);
|
||||
|
||||
// when
|
||||
|
||||
// then
|
||||
@@ -218,7 +243,9 @@ public class MappingBuilderTests extends MappingContextBaseTests {
|
||||
// given
|
||||
elasticsearchTemplate.createIndex(Book.class);
|
||||
elasticsearchTemplate.putMapping(Book.class);
|
||||
|
||||
// when
|
||||
|
||||
// then
|
||||
}
|
||||
|
||||
@@ -226,7 +253,6 @@ public class MappingBuilderTests extends MappingContextBaseTests {
|
||||
public void shouldUseBothAnalyzer() {
|
||||
|
||||
// given
|
||||
elasticsearchTemplate.deleteIndex(Book.class);
|
||||
elasticsearchTemplate.createIndex(Book.class);
|
||||
elasticsearchTemplate.putMapping(Book.class);
|
||||
|
||||
@@ -236,19 +262,18 @@ public class MappingBuilderTests extends MappingContextBaseTests {
|
||||
Map prefixDescription = (Map) ((Map) descriptionMapping.get("fields")).get("prefix");
|
||||
|
||||
// then
|
||||
assertThat(prefixDescription.size(), is(3));
|
||||
assertThat(prefixDescription.get("type"), equalTo("text"));
|
||||
assertThat(prefixDescription.get("analyzer"), equalTo("stop"));
|
||||
assertThat(prefixDescription.get("search_analyzer"), equalTo("standard"));
|
||||
assertThat(descriptionMapping.get("type"), equalTo("text"));
|
||||
assertThat(descriptionMapping.get("analyzer"), equalTo("whitespace"));
|
||||
assertThat(prefixDescription).hasSize(3);
|
||||
assertThat(prefixDescription.get("type")).isEqualTo("text");
|
||||
assertThat(prefixDescription.get("analyzer")).isEqualTo("stop");
|
||||
assertThat(prefixDescription.get("search_analyzer")).isEqualTo("standard");
|
||||
assertThat(descriptionMapping.get("type")).isEqualTo("text");
|
||||
assertThat(descriptionMapping.get("analyzer")).isEqualTo("whitespace");
|
||||
}
|
||||
|
||||
@Test // DATAES-492
|
||||
public void shouldUseKeywordNormalizer() {
|
||||
|
||||
// given
|
||||
elasticsearchTemplate.deleteIndex(NormalizerEntity.class);
|
||||
elasticsearchTemplate.createIndex(NormalizerEntity.class);
|
||||
elasticsearchTemplate.putMapping(NormalizerEntity.class);
|
||||
|
||||
@@ -259,17 +284,16 @@ public class MappingBuilderTests extends MappingContextBaseTests {
|
||||
Map fieldDescriptionLowerCase = (Map) ((Map) ((Map) properties.get("description")).get("fields")).get("lower_case");
|
||||
|
||||
// then
|
||||
assertThat(fieldName.get("type"), equalTo("keyword"));
|
||||
assertThat(fieldName.get("normalizer"), equalTo("lower_case_normalizer"));
|
||||
assertThat(fieldDescriptionLowerCase.get("type"), equalTo("keyword"));
|
||||
assertThat(fieldDescriptionLowerCase.get("normalizer"), equalTo("lower_case_normalizer"));
|
||||
assertThat(fieldName.get("type")).isEqualTo("keyword");
|
||||
assertThat(fieldName.get("normalizer")).isEqualTo("lower_case_normalizer");
|
||||
assertThat(fieldDescriptionLowerCase.get("type")).isEqualTo("keyword");
|
||||
assertThat(fieldDescriptionLowerCase.get("normalizer")).isEqualTo("lower_case_normalizer");
|
||||
}
|
||||
|
||||
@Test // DATAES-503
|
||||
public void shouldUseCopyTo() {
|
||||
|
||||
// given
|
||||
elasticsearchTemplate.deleteIndex(CopyToEntity.class);
|
||||
elasticsearchTemplate.createIndex(CopyToEntity.class);
|
||||
elasticsearchTemplate.putMapping(CopyToEntity.class);
|
||||
|
||||
@@ -281,8 +305,8 @@ public class MappingBuilderTests extends MappingContextBaseTests {
|
||||
|
||||
// then
|
||||
List<String> copyToValue = Collections.singletonList("name");
|
||||
assertThat(fieldFirstName.get("copy_to"), equalTo(copyToValue));
|
||||
assertThat(fieldLastName.get("copy_to"), equalTo(copyToValue));
|
||||
assertThat(fieldFirstName.get("copy_to")).isEqualTo(copyToValue);
|
||||
assertThat(fieldLastName.get("copy_to")).isEqualTo(copyToValue);
|
||||
}
|
||||
|
||||
@Test // DATAES-568
|
||||
@@ -455,7 +479,7 @@ public class MappingBuilderTests extends MappingContextBaseTests {
|
||||
/**
|
||||
* MinimalChildEntity
|
||||
*
|
||||
* @author Peter-josef Meisch
|
||||
* @author Peter-Josef Meisch
|
||||
*/
|
||||
@Document(indexName = "test-index-minimal", type = "mapping")
|
||||
static class MinimalChildEntity {
|
||||
@@ -464,4 +488,307 @@ public class MappingBuilderTests extends MappingContextBaseTests {
|
||||
|
||||
@Parent(type = "parentType") private String parentId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Rizwan Idrees
|
||||
* @author Mohsin Husen
|
||||
* @author Nordine Bittich
|
||||
*/
|
||||
@Setter
|
||||
@Getter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@Document(indexName = "test-index-book-mapping-builder", type = "book", shards = 1, replicas = 0,
|
||||
refreshInterval = "-1")
|
||||
static class Book {
|
||||
|
||||
@Id private String id;
|
||||
private String name;
|
||||
@Field(type = FieldType.Object) private Author author;
|
||||
@Field(type = FieldType.Nested) private Map<Integer, Collection<String>> buckets = new HashMap<>();
|
||||
@MultiField(mainField = @Field(type = FieldType.Text, analyzer = "whitespace"),
|
||||
otherFields = { @InnerField(suffix = "prefix", type = FieldType.Text, analyzer = "stop",
|
||||
searchAnalyzer = "standard") }) private String description;
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Stuart Stevenson
|
||||
* @author Mohsin Husen
|
||||
*/
|
||||
@Document(indexName = "test-index-simple-recursive-mapping-builder", type = "circular-object", shards = 1,
|
||||
replicas = 0, refreshInterval = "-1")
|
||||
static class SimpleRecursiveEntity {
|
||||
|
||||
@Id private String id;
|
||||
@Field(type = FieldType.Object, ignoreFields = { "circularObject" }) private SimpleRecursiveEntity circularObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Sascha Woo
|
||||
*/
|
||||
@Setter
|
||||
@Getter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@Document(indexName = "test-copy-to-mapping-builder", type = "test", shards = 1, replicas = 0, refreshInterval = "-1")
|
||||
static class CopyToEntity {
|
||||
|
||||
@Id private String id;
|
||||
|
||||
@Field(type = FieldType.Keyword, copyTo = "name") private String firstName;
|
||||
|
||||
@Field(type = FieldType.Keyword, copyTo = "name") private String lastName;
|
||||
|
||||
@Field(type = FieldType.Keyword) private String name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Sascha Woo
|
||||
*/
|
||||
@Setter
|
||||
@Getter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@Document(indexName = "test-index-normalizer-mapping-builder", type = "test", shards = 1, replicas = 0,
|
||||
refreshInterval = "-1")
|
||||
@Setting(settingPath = "/settings/test-normalizer.json")
|
||||
static class NormalizerEntity {
|
||||
|
||||
@Id private String id;
|
||||
|
||||
@Field(type = FieldType.Keyword, normalizer = "lower_case_normalizer") private String name;
|
||||
|
||||
@MultiField(mainField = @Field(type = FieldType.Text), otherFields = { @InnerField(suffix = "lower_case",
|
||||
type = FieldType.Keyword, normalizer = "lower_case_normalizer") }) private String description;
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Rizwan Idrees
|
||||
* @author Mohsin Husen
|
||||
*/
|
||||
static class Author {
|
||||
|
||||
private String id;
|
||||
private String name;
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Kevin Leturc
|
||||
*/
|
||||
@Document(indexName = "test-index-sample-inherited-mapping-builder", type = "mapping", shards = 1, replicas = 0,
|
||||
refreshInterval = "-1")
|
||||
static class SampleInheritedEntity extends AbstractInheritedEntity {
|
||||
|
||||
@Field(type = Text, index = false, store = true, analyzer = "standard") private String message;
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public void setMessage(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Kevin Leturc
|
||||
*/
|
||||
static class SampleInheritedEntityBuilder {
|
||||
|
||||
private SampleInheritedEntity result;
|
||||
|
||||
public SampleInheritedEntityBuilder(String id) {
|
||||
result = new SampleInheritedEntity();
|
||||
result.setId(id);
|
||||
}
|
||||
|
||||
public SampleInheritedEntityBuilder createdDate(Date createdDate) {
|
||||
result.setCreatedDate(createdDate);
|
||||
return this;
|
||||
}
|
||||
|
||||
public SampleInheritedEntityBuilder message(String message) {
|
||||
result.setMessage(message);
|
||||
return this;
|
||||
}
|
||||
|
||||
public SampleInheritedEntity build() {
|
||||
return result;
|
||||
}
|
||||
|
||||
public IndexQuery buildIndex() {
|
||||
IndexQuery indexQuery = new IndexQuery();
|
||||
indexQuery.setId(result.getId());
|
||||
indexQuery.setObject(result);
|
||||
return indexQuery;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Artur Konczak
|
||||
* @author Mohsin Husen
|
||||
*/
|
||||
@Setter
|
||||
@Getter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@Document(indexName = "test-index-stock-mapping-builder", type = "price", shards = 1, replicas = 0,
|
||||
refreshInterval = "-1")
|
||||
static class StockPrice {
|
||||
|
||||
@Id private String id;
|
||||
|
||||
private String symbol;
|
||||
|
||||
@Field(type = FieldType.Double) private BigDecimal price;
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Kevin Letur
|
||||
*/
|
||||
static class AbstractInheritedEntity {
|
||||
|
||||
@Id private String id;
|
||||
|
||||
@Field(type = FieldType.Date, index = false) private Date createdDate;
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Date getCreatedDate() {
|
||||
return createdDate;
|
||||
}
|
||||
|
||||
public void setCreatedDate(Date createdDate) {
|
||||
this.createdDate = createdDate;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Jakub Vavrik
|
||||
*/
|
||||
@Document(indexName = "test-index-recursive-mapping-mapping-builder", type = "mapping", shards = 1, replicas = 0,
|
||||
refreshInterval = "-1")
|
||||
static class SampleTransientEntity {
|
||||
|
||||
@Id private String id;
|
||||
|
||||
@Field(type = Text, index = false, store = true, analyzer = "standard") private String message;
|
||||
|
||||
@Transient private SampleTransientEntity.NestedEntity nested;
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public void setMessage(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
static class NestedEntity {
|
||||
|
||||
@Field private static SampleTransientEntity.NestedEntity someField = new SampleTransientEntity.NestedEntity();
|
||||
@Field private Boolean something;
|
||||
|
||||
public SampleTransientEntity.NestedEntity getSomeField() {
|
||||
return someField;
|
||||
}
|
||||
|
||||
public void setSomeField(SampleTransientEntity.NestedEntity someField) {
|
||||
this.someField = someField;
|
||||
}
|
||||
|
||||
public Boolean getSomething() {
|
||||
return something;
|
||||
}
|
||||
|
||||
public void setSomething(Boolean something) {
|
||||
this.something = something;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Artur Konczak
|
||||
*/
|
||||
@Setter
|
||||
@Getter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@Document(indexName = "test-index-geo-mapping-builder", type = "geo-test-index", shards = 1, replicas = 0,
|
||||
refreshInterval = "-1")
|
||||
static class GeoEntity {
|
||||
|
||||
@Id private String id;
|
||||
|
||||
// geo shape - Spring Data
|
||||
private Box box;
|
||||
private Circle circle;
|
||||
private Polygon polygon;
|
||||
|
||||
// geo point - Custom implementation + Spring Data
|
||||
@GeoPointField private Point pointA;
|
||||
|
||||
private GeoPoint pointB;
|
||||
|
||||
@GeoPointField private String pointC;
|
||||
|
||||
@GeoPointField private double[] pointD;
|
||||
}
|
||||
|
||||
/**
|
||||
* Created by akonczak on 21/08/2016.
|
||||
*/
|
||||
@Document(indexName = "test-index-user-mapping-builder", type = "user")
|
||||
static class User {
|
||||
@Id private String id;
|
||||
|
||||
@Field(type = FieldType.Nested, ignoreFields = { "users" }) private Set<Group> groups = new HashSet<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Created by akonczak on 21/08/2016.
|
||||
*/
|
||||
@Document(indexName = "test-index-group-mapping-builder", type = "group")
|
||||
static class Group {
|
||||
|
||||
@Id String id;
|
||||
|
||||
@Field(type = FieldType.Nested, ignoreFields = { "groups" }) private Set<User> users = new HashSet<>();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+2
-10
@@ -15,9 +15,6 @@
|
||||
|
||||
package org.springframework.data.elasticsearch.core;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.springframework.data.elasticsearch.config.ElasticsearchConfigurationSupport;
|
||||
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
|
||||
import org.springframework.data.elasticsearch.core.convert.MappingElasticsearchConverter;
|
||||
@@ -37,13 +34,8 @@ abstract class MappingContextBaseTests {
|
||||
|
||||
private SimpleElasticsearchMappingContext setupMappingContext() {
|
||||
|
||||
SimpleElasticsearchMappingContext mappingContext = new ElasticsearchConfigurationSupport() {
|
||||
@Override
|
||||
protected Collection<String> getMappingBasePackages() {
|
||||
return Collections.singletonList("org.springframework.data.elasticsearch.entities.mapping");
|
||||
}
|
||||
}.elasticsearchMappingContext();
|
||||
|
||||
SimpleElasticsearchMappingContext mappingContext = new ElasticsearchConfigurationSupport() {}
|
||||
.elasticsearchMappingContext();
|
||||
mappingContext.initialize();
|
||||
return mappingContext;
|
||||
}
|
||||
|
||||
+89
-3
@@ -17,13 +17,18 @@ package org.springframework.data.elasticsearch.core;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.elasticsearch.index.query.QueryBuilders.*;
|
||||
import static org.springframework.data.elasticsearch.annotations.FieldType.*;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import java.lang.Long;
|
||||
import java.lang.Object;
|
||||
import java.net.ConnectException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
@@ -34,15 +39,16 @@ import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import org.elasticsearch.ElasticsearchStatusException;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
import org.springframework.dao.DataAccessResourceFailureException;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.annotation.Version;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
@@ -50,6 +56,8 @@ import org.springframework.data.elasticsearch.ElasticsearchVersion;
|
||||
import org.springframework.data.elasticsearch.ElasticsearchVersionRule;
|
||||
import org.springframework.data.elasticsearch.TestUtils;
|
||||
import org.springframework.data.elasticsearch.annotations.Document;
|
||||
import org.springframework.data.elasticsearch.annotations.Field;
|
||||
import org.springframework.data.elasticsearch.annotations.Score;
|
||||
import org.springframework.data.elasticsearch.core.query.Criteria;
|
||||
import org.springframework.data.elasticsearch.core.query.CriteriaQuery;
|
||||
import org.springframework.data.elasticsearch.core.query.IndexQuery;
|
||||
@@ -57,7 +65,6 @@ import org.springframework.data.elasticsearch.core.query.IndexQueryBuilder;
|
||||
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
|
||||
import org.springframework.data.elasticsearch.core.query.SearchQuery;
|
||||
import org.springframework.data.elasticsearch.core.query.StringQuery;
|
||||
import org.springframework.data.elasticsearch.entities.SampleEntity;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.util.StringUtils;
|
||||
@@ -67,6 +74,9 @@ import org.springframework.util.StringUtils;
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
* @author Peter-Josef Meisch
|
||||
* @author Farid Azaza
|
||||
* @author Martin Choraine
|
||||
* @currentRead Golden Fool - Robin Hobb
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@@ -75,7 +85,7 @@ public class ReactiveElasticsearchTemplateTests {
|
||||
|
||||
public @Rule ElasticsearchVersionRule elasticsearchVersion = ElasticsearchVersionRule.any();
|
||||
|
||||
static final String DEFAULT_INDEX = "test-index-sample";
|
||||
static final String DEFAULT_INDEX = "reactive-template-test-index";
|
||||
static final String ALTERNATE_INDEX = "reactive-template-tests-alternate-index";
|
||||
|
||||
private ElasticsearchRestTemplate restTemplate;
|
||||
@@ -381,6 +391,43 @@ public class ReactiveElasticsearchTemplateTests {
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test // DATAES-595
|
||||
public void shouldReturnListUsingLocalPreferenceForGivenCriteria() {
|
||||
|
||||
SampleEntity sampleEntity1 = randomEntity("test message");
|
||||
SampleEntity sampleEntity2 = randomEntity("test test");
|
||||
SampleEntity sampleEntity3 = randomEntity("some message");
|
||||
|
||||
index(sampleEntity1, sampleEntity2, sampleEntity3);
|
||||
|
||||
CriteriaQuery queryWithValidPreference = new CriteriaQuery(
|
||||
new Criteria("message").contains("some").and("message").contains("message"));
|
||||
queryWithValidPreference.setPreference("_local");
|
||||
|
||||
template.find(queryWithValidPreference, SampleEntity.class) //
|
||||
.as(StepVerifier::create) //
|
||||
.expectNext(sampleEntity3) //
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test // DATAES-595
|
||||
public void shouldThrowElasticsearchStatusExceptionWhenInvalidPreferenceForGivenCriteria() {
|
||||
|
||||
SampleEntity sampleEntity1 = randomEntity("test message");
|
||||
SampleEntity sampleEntity2 = randomEntity("test test");
|
||||
SampleEntity sampleEntity3 = randomEntity("some message");
|
||||
|
||||
index(sampleEntity1, sampleEntity2, sampleEntity3);
|
||||
|
||||
CriteriaQuery queryWithInvalidPreference = new CriteriaQuery(
|
||||
new Criteria("message").contains("some").and("message").contains("message"));
|
||||
queryWithInvalidPreference.setPreference("_only_nodes:oops");
|
||||
|
||||
template.find(queryWithInvalidPreference, SampleEntity.class) //
|
||||
.as(StepVerifier::create) //
|
||||
.expectError(ElasticsearchStatusException.class).verify();
|
||||
}
|
||||
|
||||
@Test // DATAES-504
|
||||
public void shouldReturnProjectedTargetEntity() {
|
||||
|
||||
@@ -620,6 +667,30 @@ public class ReactiveElasticsearchTemplateTests {
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test // DATAES-593
|
||||
public void shouldReturnDocumentWithCollapsedField() {
|
||||
|
||||
SampleEntity entity1 = randomEntity("test message");
|
||||
entity1.setRate(1);
|
||||
SampleEntity entity2 = randomEntity("test another message");
|
||||
entity2.setRate(2);
|
||||
SampleEntity entity3 = randomEntity("test message again");
|
||||
entity3.setRate(1);
|
||||
index(entity1, entity2, entity3);
|
||||
|
||||
SearchQuery query = new NativeSearchQueryBuilder() //
|
||||
.withIndices(DEFAULT_INDEX) //
|
||||
.withQuery(matchAllQuery()) //
|
||||
.withCollapseField("rate") //
|
||||
.withPageable(PageRequest.of(0, 25)) //
|
||||
.build();
|
||||
|
||||
template.find(query, SampleEntity.class) //
|
||||
.as(StepVerifier::create) //
|
||||
.expectNextCount(2) //
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Data
|
||||
@Document(indexName = "marvel", type = "characters")
|
||||
static class Person {
|
||||
@@ -676,4 +747,19 @@ public class ReactiveElasticsearchTemplateTests {
|
||||
static class Message {
|
||||
String message;
|
||||
}
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@EqualsAndHashCode(exclude = "score")
|
||||
@Document(indexName = DEFAULT_INDEX, type = "test-type", shards = 1, replicas = 0, refreshInterval = "-1")
|
||||
static class SampleEntity {
|
||||
|
||||
@Id private String id;
|
||||
@Field(type = Text, store = true, fielddata = true) private String message;
|
||||
private int rate;
|
||||
@Version private Long version;
|
||||
@Score private float score;
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user