1
0
mirror of synced 2026-09-04 15:09:39 +00:00

Compare commits

...

18 Commits

Author SHA1 Message Date
Jens Schauder 060cab76d5 DATAES-692 - Release version 3.2.3 (Moore SR3). 2019-12-04 14:12:24 +01:00
Jens Schauder afb8a35eac DATAES-692 - Prepare 3.2.3 (Moore SR3). 2019-12-04 14:11:44 +01:00
Jens Schauder 1c0dd71020 DATAES-692 - Updated changelog. 2019-12-04 14:11:42 +01:00
Jens Schauder 1633668d7f DATAES-691 - Updated changelog. 2019-12-04 12:09:51 +01:00
Peter-Josef Meisch 6756f792c8 DATAES-699 - Fix count implementation.
Original PR: #349
2019-12-01 10:03:56 +01:00
Peter-Josef Meisch bae4db8a7f DATAES-700 - Enable proxy support for RestClient. 2019-11-30 23:30:59 +01:00
Mark Paluch c47fd2cfce DATAES-685 - After release cleanups. 2019-11-18 12:42:05 +01:00
Mark Paluch cb08bb7196 DATAES-685 - Prepare next development iteration. 2019-11-18 12:42:04 +01:00
Mark Paluch f3500623ff DATAES-685 - Release version 3.2.2 (Moore SR2). 2019-11-18 12:32:12 +01:00
Mark Paluch 4c4cbed43b DATAES-685 - Prepare 3.2.2 (Moore SR2). 2019-11-18 12:31:53 +01:00
Mark Paluch 23fb5689b7 DATAES-685 - Updated changelog. 2019-11-18 12:31:52 +01:00
Mark Paluch b551466f94 DATAES-683 - Updated changelog. 2019-11-18 12:16:28 +01:00
Peter-Josef Meisch 0a7af69d69 DATAES-680 - Fix missing import. 2019-11-08 17:03:37 +01:00
Peter-Josef Meisch 2cf42a4763 DATAES-680 - ReactiveElasticsearchTemplate-should-use-the-count-API.
Original PR: #341

(cherry picked from commit 62385edaa5)
2019-11-07 17:07:39 +01:00
Peter-Josef Meisch 6f0d1ee9e7 DATAES-684 - Polishing.
(cherry picked from commit 24751972a8)
2019-11-05 10:41:21 +01:00
Henrique Amaral f82dd229d9 DATAES-684 Implement bulk request from reactive client
Original PR: #342

* DATAES-684 Implement bulk request from reactive client

* Update src/main/java/org/springframework/data/elasticsearch/client/reactive/ReactiveElasticsearchClient.java

Co-Authored-By: Peter-Josef Meisch <pj.meisch@sothawo.com>

* DATAES-684 Implement bulk request from reactive client

Added author

(cherry picked from commit 6ae424428c)
2019-11-05 10:39:40 +01:00
Christoph Strobl 3b833f6f63 DATAES-662 - After release cleanups. 2019-11-04 15:34:27 +01:00
Christoph Strobl 2517fd5c90 DATAES-662 - Prepare next development iteration. 2019-11-04 15:34:26 +01:00
19 changed files with 541 additions and 89 deletions
+23 -5
View File
@@ -5,12 +5,12 @@
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-elasticsearch</artifactId>
<version>3.2.1.RELEASE</version>
<version>3.2.3.RELEASE</version>
<parent>
<groupId>org.springframework.data.build</groupId>
<artifactId>spring-data-parent</artifactId>
<version>2.2.1.RELEASE</version>
<version>2.2.3.RELEASE</version>
</parent>
<name>Spring Data Elasticsearch</name>
@@ -21,7 +21,7 @@
<commonslang>2.6</commonslang>
<elasticsearch>6.8.4</elasticsearch>
<log4j>2.9.1</log4j>
<springdata.commons>2.2.1.RELEASE</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>
@@ -248,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>
@@ -258,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>
@@ -144,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
*/
@@ -212,8 +220,8 @@ public interface ClientConfiguration {
TerminalClientConfigurationBuilder usingSsl(SSLContext sslContext);
/**
* Connect via {@literal https} using the givens {@link SSLContext} and HostnameVerifier {@link HostnameVerifier} .<br />
*
* 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)}.
*
@@ -286,6 +294,12 @@ public interface ClientConfiguration {
*/
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.
*
@@ -53,6 +53,7 @@ class ClientConfigurationBuilder
private Duration soTimeout = Duration.ofSeconds(5);
private String username;
private String password;
private String proxy;
/*
* (non-Javadoc)
@@ -81,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()
@@ -189,8 +197,8 @@ class ClientConfigurationBuilder
headers.setBasicAuth(username, password);
}
return new DefaultClientConfiguration(this.hosts, this.headers, this.useSsl, this.sslContext, this.soTimeout,
this.connectTimeout, this.hostnameVerifier);
return new DefaultClientConfiguration(hosts, headers, useSsl, sslContext, soTimeout, connectTimeout,
hostnameVerifier, proxy);
}
private static InetSocketAddress parse(String hostAndPort) {
@@ -44,9 +44,11 @@ class DefaultClientConfiguration implements ClientConfiguration {
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 HostnameVerifier hostnameVerifier) {
@Nullable SSLContext sslContext, Duration soTimeout, Duration connectTimeout,
@Nullable HostnameVerifier hostnameVerifier, String proxy) {
this.hosts = Collections.unmodifiableList(new ArrayList<>(hosts));
this.headers = new HttpHeaders(headers);
@@ -55,6 +57,7 @@ class DefaultClientConfiguration implements ClientConfiguration {
this.soTimeout = soTimeout;
this.connectTimeout = connectTimeout;
this.hostnameVerifier = hostnameVerifier;
this.proxy = proxy;
}
/*
@@ -120,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);
}
}
@@ -118,6 +118,8 @@ public final class RestClients {
clientBuilder.setDefaultRequestConfig(requestConfigBuilder.build());
clientConfiguration.getProxy().map(HttpHost::create).ifPresent(clientBuilder::setProxy);
return clientBuilder;
});
@@ -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;
}
}
/**
@@ -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
@@ -60,6 +60,7 @@ 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.Priority;
import org.elasticsearch.common.Strings;
@@ -98,6 +99,8 @@ import org.springframework.lang.Nullable;
* <p>
* Only intended for internal use.
*
* @author Christoph Strobl
* @author Peter-Josef Meisch
* @since 3.2
*/
public class RequestConverters {
@@ -386,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());
@@ -598,6 +598,7 @@ public class ElasticsearchRestTemplate extends AbstractElasticsearchTemplate
if (elasticsearchQuery != null) {
sourceBuilder.query(elasticsearchQuery);
}
sourceBuilder.size(0);
countRequest.source(sourceBuilder);
try {
@@ -616,6 +617,7 @@ public class ElasticsearchRestTemplate extends AbstractElasticsearchTemplate
if (elasticsearchFilter != null) {
searchRequest.source().postFilter(elasticsearchFilter);
}
searchRequest.source().size(0);
SearchResponse response;
try {
response = client.search(searchRequest, RequestOptions.DEFAULT);
@@ -508,6 +508,7 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate
if (elasticsearchQuery != null) {
countRequestBuilder.setQuery(elasticsearchQuery);
}
countRequestBuilder.setSize(0);
return countRequestBuilder.execute().actionGet().getHits().getTotalHits();
}
@@ -521,6 +522,7 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate
if (elasticsearchFilter != null) {
searchRequestBuilder.setPostFilter(elasticsearchFilter);
}
searchRequestBuilder.setSize(0);
return searchRequestBuilder.execute().actionGet().getHits().getTotalHits();
}
@@ -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;
@@ -247,80 +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());
}
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);
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)
@@ -457,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.
@@ -557,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))) //
@@ -665,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()));
}
+26
View File
@@ -1,6 +1,30 @@
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.
@@ -898,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
+2 -1
View File
@@ -1,4 +1,4 @@
Spring Data Elasticsearch 3.2.1
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.
@@ -44,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();
@@ -54,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));
@@ -62,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
@@ -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();
}
}
}
@@ -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;
@@ -65,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")
@@ -653,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);
}
@@ -20,6 +20,7 @@ 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();
}
}
@@ -60,6 +60,7 @@ import org.springframework.web.util.UriBuilder;
/**
* @author Christoph Strobl
* @author Henrique Amaral
*/
public class ReactiveMockClientTestsUtils {
@@ -361,6 +362,12 @@ public class ReactiveMockClientTestsUtils {
});
}
default Receive receiveBulkOk() {
return receiveJsonFromFile("bulk-ok") //
.receive(Receive::ok);
}
}
public interface Receive {
@@ -0,0 +1,23 @@
{
"took": 30,
"errors": false,
"items": [
{
"update": {
"_index": "twitter",
"_type": "doc",
"_id": "1",
"_version": 2,
"result": "updated",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 2,
"_primary_term": 4
}
}
]
}