Remove the integration of the deprecated RestHighLevelClient.

Original Pull Request #2560
Closes #2558
This commit is contained in:
Peter-Josef Meisch 2023-05-13 09:07:52 +02:00 committed by GitHub
parent 73e9a6f5c5
commit f464f77985
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
148 changed files with 142 additions and 23518 deletions

50
pom.xml
View File

@ -20,17 +20,12 @@
<properties>
<springdata.commons>3.2.0-SNAPSHOT</springdata.commons>
<!-- version of the RestHighLevelClient -->
<elasticsearch-rhlc>7.17.9</elasticsearch-rhlc>
<!-- version of the new ElasticsearchClient -->
<!-- version of the ElasticsearchClient -->
<elasticsearch-java>8.7.1</elasticsearch-java>
<log4j>2.18.0</log4j>
<!-- netty dependency can be removed once the WebClient code is gone -->
<netty>4.1.90.Final</netty>
<blockhound-junit>1.0.8.RELEASE</blockhound-junit>
<hoverfly>0.14.4</hoverfly>
<log4j>2.18.0</log4j>
<jsonassert>1.5.1</jsonassert>
<testcontainers>1.18.0</testcontainers>
<wiremock>2.35.0</wiremock>
@ -93,18 +88,6 @@
<url>https://github.com/spring-projects/spring-data-elasticsearch/issues</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 -->
@ -132,33 +115,12 @@
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.projectreactor.netty</groupId>
<artifactId>reactor-netty-http</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<scope>test</scope>
</dependency>
<!-- optional Elasticsearch RestHighLevelClient, deprecated in SDE 5.0 -->
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>${elasticsearch-rhlc}</version>
<optional>true</optional>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- new Elasticsearch client, needs the low-level rest client and json api -->
<dependency>
<groupId>co.elastic.clients</groupId>
<artifactId>elasticsearch-java</artifactId>
@ -340,6 +302,14 @@
<scope>test</scope>
</dependency>
<!--we need Murmur3Hash in a test, before 5.2 we had it from the old Elasticsearch dependency -->
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.15</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>

View File

@ -15,7 +15,7 @@
*/
package org.springframework.data.elasticsearch.client.elc;
import static org.springframework.data.elasticsearch.client.elc.TypeUtils.*;
import static org.springframework.data.elasticsearch.client.elc.TypeUtils.result;
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.Time;
@ -28,35 +28,20 @@ import co.elastic.clients.transport.Version;
import java.io.IOException;
import java.time.Duration;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.stream.Collectors;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.data.elasticsearch.BulkFailureException;
import org.springframework.data.elasticsearch.client.UnsupportedBackendOperation;
import org.springframework.data.elasticsearch.core.AbstractElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.IndexOperations;
import org.springframework.data.elasticsearch.core.IndexedObjectInformation;
import org.springframework.data.elasticsearch.core.MultiGetItem;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.SearchScrollHits;
import org.springframework.data.elasticsearch.core.*;
import org.springframework.data.elasticsearch.core.cluster.ClusterOperations;
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
import org.springframework.data.elasticsearch.core.document.Document;
import org.springframework.data.elasticsearch.core.document.SearchDocumentResponse;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.BulkOptions;
import org.springframework.data.elasticsearch.core.query.ByQueryResponse;
import org.springframework.data.elasticsearch.core.query.IndexQuery;
import org.springframework.data.elasticsearch.core.query.MoreLikeThisQuery;
import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.data.elasticsearch.core.query.SearchTemplateQuery;
import org.springframework.data.elasticsearch.core.query.UpdateQuery;
import org.springframework.data.elasticsearch.core.query.*;
import org.springframework.data.elasticsearch.core.query.UpdateResponse;
import org.springframework.data.elasticsearch.core.reindex.ReindexRequest;
import org.springframework.data.elasticsearch.core.reindex.ReindexResponse;
@ -638,6 +623,11 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
return NativeQuery.builder().withQuery(qb -> qb.ids(iq -> iq.values(ids))).build();
}
@Override
public BaseQueryBuilder queryBuilderWithIds(List<String> ids) {
return NativeQuery.builder().withIds(ids);
}
/**
* extract the list of {@link IndexedObjectInformation} from a {@link BulkResponse}.
*

View File

@ -100,7 +100,7 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
getRefreshPolicy());
return Mono.just(entity) //
.zipWith(//
Mono.from(execute((ClientCallback<Publisher<IndexResponse>>) client -> client.index(indexRequest))) //
Mono.from(execute(client -> client.index(indexRequest))) //
.map(indexResponse -> new IndexResponseMetaData(indexResponse.id(), //
indexResponse.index(), //
indexResponse.seqNo(), //
@ -149,9 +149,7 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
GetRequest getRequest = requestConverter.documentGetRequest(id, routingResolver.getRouting(), index, true);
return Mono.from(execute(
((ClientCallback<Publisher<GetResponse<EntityAsMap>>>) client -> client.get(getRequest, EntityAsMap.class))))
.map(GetResult::found) //
return Mono.from(execute(client -> client.get(getRequest, EntityAsMap.class))).map(GetResult::found) //
.onErrorReturn(NoSuchIndexException.class, false);
}
@ -162,9 +160,7 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
DeleteByQueryRequest request = requestConverter.documentDeleteByQueryRequest(query, routingResolver.getRouting(),
entityType, index, getRefreshPolicy());
return Mono
.from(execute((ClientCallback<Publisher<DeleteByQueryResponse>>) client -> client.deleteByQuery(request)))
.map(responseConverter::byQueryResponse);
return Mono.from(execute(client -> client.deleteByQuery(request))).map(responseConverter::byQueryResponse);
}
@Override
@ -176,8 +172,8 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
GetRequest getRequest = requestConverter.documentGetRequest(id, routingResolver.getRouting(), index, false);
Mono<GetResponse<EntityAsMap>> getResponse = Mono.from(execute(
(ClientCallback<Publisher<GetResponse<EntityAsMap>>>) client -> client.get(getRequest, EntityAsMap.class)));
Mono<GetResponse<EntityAsMap>> getResponse = Mono
.from(execute(client -> client.get(getRequest, EntityAsMap.class)));
ReadDocumentCallback<T> callback = new ReadDocumentCallback<>(converter, entityType, index);
return getResponse.flatMap(response -> callback.toEntity(DocumentAdapters.from(response)));
@ -192,9 +188,7 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
true);
return Mono.from(execute( //
(ClientCallback<Publisher<co.elastic.clients.elasticsearch.core.ReindexResponse>>) client -> client
.reindex(reindexRequestES)))
.map(responseConverter::reindexResponse);
client -> client.reindex(reindexRequestES))).map(responseConverter::reindexResponse);
}
@Override
@ -206,8 +200,7 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
false);
return Mono.from(execute( //
(ClientCallback<Publisher<co.elastic.clients.elasticsearch.core.ReindexResponse>>) client -> client
.reindex(reindexRequestES)))
client -> client.reindex(reindexRequestES)))
.flatMap(response -> (response.task() == null)
? Mono.error(
new UnsupportedBackendOperation("ElasticsearchClient did not return a task id on submit request"))
@ -223,13 +216,10 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
UpdateRequest<Document, ?> request = requestConverter.documentUpdateRequest(updateQuery, index, getRefreshPolicy(),
routingResolver.getRouting());
return Mono.from(execute(
(ClientCallback<Publisher<co.elastic.clients.elasticsearch.core.UpdateResponse<Document>>>) client -> client
.update(request, Document.class)))
.flatMap(response -> {
UpdateResponse.Result result = result(response.result());
return result == null ? Mono.empty() : Mono.just(UpdateResponse.of(result));
});
return Mono.from(execute(client -> client.update(request, Document.class))).flatMap(response -> {
UpdateResponse.Result result = result(response.result());
return result == null ? Mono.empty() : Mono.just(UpdateResponse.of(result));
});
}
@Override
@ -292,7 +282,7 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
private Mono<String> doDelete(DeleteRequest request) {
return Mono.from(execute((ClientCallback<Publisher<DeleteResponse>>) client -> client.delete(request))) //
return Mono.from(execute(client -> client.delete(request))) //
.flatMap(deleteResponse -> {
if (deleteResponse.result() == Result.NotFound) {
return Mono.empty();
@ -311,8 +301,7 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
ReadDocumentCallback<T> callback = new ReadDocumentCallback<>(converter, clazz, index);
Publisher<MgetResponse<EntityAsMap>> response = execute(
(ClientCallback<Publisher<MgetResponse<EntityAsMap>>>) client -> client.mget(request, EntityAsMap.class));
Publisher<MgetResponse<EntityAsMap>> response = execute(client -> client.mget(request, EntityAsMap.class));
return Mono.from(response)//
.flatMapMany(it -> Flux.fromIterable(DocumentAdapters.from(it))) //
@ -364,14 +353,14 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
BiFunction<PitSearchAfter, Throwable, Publisher<?>> asyncError = (psa, ex) -> {
if (LOGGER.isErrorEnabled()) {
LOGGER.error(String.format("Error during pit/search_after"), ex);
LOGGER.error("Error during pit/search_after", ex);
}
return cleanupPit(psa);
};
Function<PitSearchAfter, Publisher<?>> asyncCancel = psa -> {
if (LOGGER.isWarnEnabled()) {
LOGGER.warn(String.format("pit/search_after was cancelled"));
LOGGER.warn("pit/search_after was cancelled");
}
return cleanupPit(psa);
};
@ -383,8 +372,8 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
SearchRequest firstSearchRequest = requestConverter.searchRequest(baseQuery, routingResolver.getRouting(),
clazz, index, false, true);
return Mono.from(execute((ClientCallback<Publisher<ResponseBody<EntityAsMap>>>) client -> client
.search(firstSearchRequest, EntityAsMap.class))).expand(entityAsMapSearchResponse -> {
return Mono.from(execute(client -> client.search(firstSearchRequest, EntityAsMap.class)))
.expand(entityAsMapSearchResponse -> {
var hits = entityAsMapSearchResponse.hits().hits();
if (CollectionUtils.isEmpty(hits)) {
@ -396,8 +385,7 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
baseQuery.setSearchAfter(sortOptions);
SearchRequest followSearchRequest = requestConverter.searchRequest(baseQuery,
routingResolver.getRouting(), clazz, index, false, true);
return Mono.from(execute((ClientCallback<Publisher<ResponseBody<EntityAsMap>>>) client -> client
.search(followSearchRequest, EntityAsMap.class)));
return Mono.from(execute(client -> client.search(followSearchRequest, EntityAsMap.class)));
});
};
@ -454,9 +442,7 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
SearchRequest searchRequest = requestConverter.searchRequest(query, routingResolver.getRouting(), entityType, index,
true);
return Mono
.from(execute((ClientCallback<Publisher<ResponseBody<EntityAsMap>>>) client -> client.search(searchRequest,
EntityAsMap.class)))
return Mono.from(execute(client -> client.search(searchRequest, EntityAsMap.class)))
.map(searchResponse -> searchResponse.hits().total() != null ? searchResponse.hits().total().value() : 0L);
}
@ -465,9 +451,7 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
SearchRequest searchRequest = requestConverter.searchRequest(query, routingResolver.getRouting(), clazz, index,
false, false);
return Mono
.from(execute((ClientCallback<Publisher<ResponseBody<EntityAsMap>>>) client -> client.search(searchRequest,
EntityAsMap.class))) //
return Mono.from(execute(client -> client.search(searchRequest, EntityAsMap.class))) //
.flatMapIterable(entityAsMapSearchResponse -> entityAsMapSearchResponse.hits().hits()) //
.map(entityAsMapHit -> DocumentAdapters.from(entityAsMapHit, jsonpMapper));
}
@ -476,9 +460,7 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
var request = requestConverter.searchTemplate(query, routingResolver.getRouting(), index);
return Mono
.from(execute((ClientCallback<Publisher<SearchTemplateResponse<EntityAsMap>>>) client -> client
.searchTemplate(request, EntityAsMap.class))) //
return Mono.from(execute(client -> client.searchTemplate(request, EntityAsMap.class))) //
.flatMapIterable(entityAsMapSearchResponse -> entityAsMapSearchResponse.hits().hits()) //
.map(entityAsMapHit -> DocumentAdapters.from(entityAsMapHit, jsonpMapper));
}
@ -497,9 +479,7 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
SearchDocumentResponse.EntityCreator<T> entityCreator = searchDocument -> callback.toEntity(searchDocument)
.toFuture();
return Mono
.from(execute((ClientCallback<Publisher<ResponseBody<EntityAsMap>>>) client -> client.search(searchRequest,
EntityAsMap.class)))
return Mono.from(execute(client -> client.search(searchRequest, EntityAsMap.class)))
.map(searchResponse -> SearchDocumentResponseBuilder.from(searchResponse, entityCreator, jsonpMapper));
}
@ -520,9 +500,7 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
Assert.notNull(ignoreUnavailable, "ignoreUnavailable must not be null");
var request = requestConverter.searchOpenPointInTimeRequest(index, keepAlive, ignoreUnavailable);
return Mono
.from(execute((ClientCallback<Publisher<OpenPointInTimeResponse>>) client -> client.openPointInTime(request)))
.map(OpenPointInTimeResponse::id);
return Mono.from(execute(client -> client.openPointInTime(request))).map(OpenPointInTimeResponse::id);
}
@Override
@ -531,9 +509,7 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
Assert.notNull(pit, "pit must not be null");
ClosePointInTimeRequest request = requestConverter.searchClosePointInTime(pit);
return Mono
.from(execute((ClientCallback<Publisher<ClosePointInTimeResponse>>) client -> client.closePointInTime(request)))
.map(ClosePointInTimeResponse::succeeded);
return Mono.from(execute(client -> client.closePointInTime(request))).map(ClosePointInTimeResponse::succeeded);
}
// endregion
@ -545,8 +521,7 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
Assert.notNull(script, "script must not be null");
var request = requestConverter.scriptPut(script);
return Mono.from(execute((ClientCallback<Publisher<PutScriptResponse>>) client -> client.putScript(request)))
.map(PutScriptResponse::acknowledged);
return Mono.from(execute(client -> client.putScript(request))).map(PutScriptResponse::acknowledged);
}
@Override
@ -555,8 +530,7 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
Assert.notNull(name, "name must not be null");
var request = requestConverter.scriptGet(name);
return Mono.from(execute((ClientCallback<Publisher<GetScriptResponse>>) client -> client.getScript(request)))
.mapNotNull(responseConverter::scriptResponse);
return Mono.from(execute(client -> client.getScript(request))).mapNotNull(responseConverter::scriptResponse);
}
@Override
@ -564,8 +538,7 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
Assert.notNull(name, "name must not be null");
var request = requestConverter.scriptDelete(name);
return Mono.from(execute((ClientCallback<Publisher<DeleteScriptResponse>>) client -> client.deleteScript(request)))
.map(DeleteScriptResponse::acknowledged);
return Mono.from(execute(client -> client.deleteScript(request))).map(DeleteScriptResponse::acknowledged);
}
// endregion
@ -588,22 +561,6 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
})).map(infoResponse -> infoResponse.version().number());
}
@Override
@Deprecated
public <T> Publisher<T> execute(ReactiveElasticsearchOperations.ClientCallback<Publisher<T>> callback) {
throw new UnsupportedBackendOperation("direct execution on the WebClient is not supported for this class");
}
@Override
public <T> Publisher<T> executeWithIndicesClient(IndicesClientCallback<Publisher<T>> callback) {
throw new UnsupportedOperationException("not implemented");
}
@Override
public <T> Publisher<T> executeWithClusterClient(ClusterClientCallback<Publisher<T>> callback) {
throw new UnsupportedOperationException("not implemented");
}
@Override
public ReactiveIndexOperations indexOps(IndexCoordinates index) {
return new ReactiveIndicesTemplate(client.indices(), getReactiveClusterTemplate(), converter, index);
@ -619,9 +576,9 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
return getReactiveClusterTemplate();
}
/**
* @since 5.1
*/
/**
* @since 5.1
*/
private ReactiveClusterTemplate getReactiveClusterTemplate() {
return new ReactiveClusterTemplate(client.cluster(), converter);
}
@ -633,11 +590,16 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
@Override
public Query idsQuery(List<String> ids) {
return NativeQuery.builder().withQuery(Queries.idsQueryAsQuery(ids)).build();
return NativeQuery.builder().withQuery(qb -> qb.ids(iq -> iq.values(ids))).build();
}
@Override
public BaseQueryBuilder queryBuilderWithIds(List<String> ids) {
return NativeQuery.builder().withIds(ids);
}
/**
* Callback interface to be used with {@link #execute(ReactiveElasticsearchOperations.ClientCallback)} for operating
* Callback interface to be used with {@link #execute(ReactiveElasticsearchTemplate.ClientCallback<>)} for operating
* directly on {@link ReactiveElasticsearchClient}.
*
* @param <T>

View File

@ -1,56 +0,0 @@
/*
* Copyright 2018-2023 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.erhlc;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.context.annotation.Bean;
import org.springframework.data.elasticsearch.config.ElasticsearchConfigurationSupport;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
/**
* @author Christoph Strobl
* @author Peter-Josef Meisch
* @since 3.2
* @see ElasticsearchConfigurationSupport
* @deprecated since 5.0
*/
@Deprecated
public abstract class AbstractElasticsearchConfiguration extends ElasticsearchConfigurationSupport {
/**
* Return the {@link RestHighLevelClient} instance used to connect to the cluster. <br />
*
* @return never {@literal null}.
*/
@Bean
public abstract RestHighLevelClient elasticsearchClient();
/**
* Creates {@link ElasticsearchOperations}.
*
* @return never {@literal null}.
*/
@Bean(name = { "elasticsearchOperations", "elasticsearchTemplate" })
public ElasticsearchOperations elasticsearchOperations(ElasticsearchConverter elasticsearchConverter,
RestHighLevelClient elasticsearchClient) {
ElasticsearchRestTemplate template = new ElasticsearchRestTemplate(elasticsearchClient, elasticsearchConverter);
template.setRefreshPolicy(refreshPolicy());
return template;
}
}

View File

@ -1,81 +0,0 @@
/*
* Copyright 2018-2023 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.erhlc;
import org.elasticsearch.action.support.IndicesOptions;
import org.springframework.context.annotation.Bean;
import org.springframework.data.elasticsearch.config.ElasticsearchConfigurationSupport;
import org.springframework.data.elasticsearch.core.ReactiveElasticsearchOperations;
import org.springframework.data.elasticsearch.core.RefreshPolicy;
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
import org.springframework.lang.Nullable;
/**
* @author Christoph Strobl
* @author Peter-Josef Meisch
* @since 3.2
* @see ElasticsearchConfigurationSupport
* @deprecated since 5.0
*/
@Deprecated
public abstract class AbstractReactiveElasticsearchConfiguration extends ElasticsearchConfigurationSupport {
/**
* Return the {@link ReactiveElasticsearchClient} instance used to connect to the cluster. <br />
*
* @return never {@literal null}.
*/
@Bean
public abstract ReactiveElasticsearchClient reactiveElasticsearchClient();
/**
* Creates {@link ReactiveElasticsearchOperations}.
*
* @return never {@literal null}.
*/
@Bean
public ReactiveElasticsearchOperations reactiveElasticsearchTemplate(ElasticsearchConverter elasticsearchConverter,
ReactiveElasticsearchClient reactiveElasticsearchClient) {
ReactiveElasticsearchTemplate template = new ReactiveElasticsearchTemplate(reactiveElasticsearchClient,
elasticsearchConverter);
template.setIndicesOptions(indicesOptions());
template.setRefreshPolicy(refreshPolicy());
return template;
}
/**
* Set up the write {@link RefreshPolicy}. Default is set to null to use the cluster defaults..
*
* @return {@literal null} to use the server defaults.
*/
@Nullable
protected RefreshPolicy refreshPolicy() {
return null;
}
/**
* Set up the read {@link IndicesOptions}. Default is set to {@link IndicesOptions#strictExpandOpenAndForbidClosed()}.
*
* @return {@literal null} to use the server defaults.
*/
@Nullable
protected IndicesOptions indicesOptions() {
return IndicesOptions.strictExpandOpenAndForbidClosed();
}
}

View File

@ -1,273 +0,0 @@
/*
* Copyright 2013-2023 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.erhlc;
import static org.springframework.data.elasticsearch.core.query.Criteria.*;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.stream.Collectors;
import org.elasticsearch.common.geo.GeoDistance;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.GeoBoundingBoxQueryBuilder;
import org.elasticsearch.index.query.GeoDistanceQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.springframework.data.elasticsearch.core.geo.GeoBox;
import org.springframework.data.elasticsearch.core.geo.GeoJson;
import org.springframework.data.elasticsearch.core.geo.GeoPoint;
import org.springframework.data.elasticsearch.core.query.Criteria;
import org.springframework.data.geo.Box;
import org.springframework.data.geo.Distance;
import org.springframework.data.geo.Metrics;
import org.springframework.data.geo.Point;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
* CriteriaFilterProcessor
*
* @author Franck Marchand
* @author Mohsin Husen
* @author Artur Konczak
* @author Peter-Josef Meisch
* @deprecated since 5.0
*/
@Deprecated
class CriteriaFilterProcessor {
@Nullable
QueryBuilder createFilter(Criteria criteria) {
List<QueryBuilder> filterBuilders = new ArrayList<>();
for (Criteria chainedCriteria : criteria.getCriteriaChain()) {
if (chainedCriteria.isOr()) {
BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
queriesForEntries(chainedCriteria).forEach(boolQuery::should);
filterBuilders.add(boolQuery);
} else if (chainedCriteria.isNegating()) {
List<QueryBuilder> negationFilters = buildNegationFilter(criteria.getField().getName(),
criteria.getFilterCriteriaEntries().iterator());
filterBuilders.addAll(negationFilters);
} else {
filterBuilders.addAll(queriesForEntries(chainedCriteria));
}
}
QueryBuilder filter = null;
if (!filterBuilders.isEmpty()) {
if (filterBuilders.size() == 1) {
filter = filterBuilders.get(0);
} else {
BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
filterBuilders.forEach(boolQuery::must);
filter = boolQuery;
}
}
return filter;
}
private List<QueryBuilder> queriesForEntries(Criteria criteria) {
Assert.notNull(criteria.getField(), "criteria must have a field");
String fieldName = criteria.getField().getName();
Assert.notNull(fieldName, "Unknown field");
return criteria.getFilterCriteriaEntries().stream()
.map(entry -> queryFor(entry.getKey(), entry.getValue(), fieldName)).collect(Collectors.toList());
}
@Nullable
private QueryBuilder queryFor(OperationKey key, Object value, String fieldName) {
QueryBuilder filter = null;
switch (key) {
case WITHIN -> {
Assert.isTrue(value instanceof Object[], "Value of a geo distance filter should be an array of two values.");
filter = withinQuery(fieldName, (Object[]) value);
}
case BBOX -> {
Assert.isTrue(value instanceof Object[],
"Value of a boundedBy filter should be an array of one or two values.");
filter = boundingBoxQuery(fieldName, (Object[]) value);
}
case GEO_INTERSECTS -> {
Assert.isTrue(value instanceof GeoJson<?>, "value of a GEO_INTERSECTS filter must be a GeoJson object");
filter = geoJsonQuery(fieldName, (GeoJson<?>) value, "intersects");
}
case GEO_IS_DISJOINT -> {
Assert.isTrue(value instanceof GeoJson<?>, "value of a GEO_IS_DISJOINT filter must be a GeoJson object");
filter = geoJsonQuery(fieldName, (GeoJson<?>) value, "disjoint");
}
case GEO_WITHIN -> {
Assert.isTrue(value instanceof GeoJson<?>, "value of a GEO_WITHIN filter must be a GeoJson object");
filter = geoJsonQuery(fieldName, (GeoJson<?>) value, "within");
}
case GEO_CONTAINS -> {
Assert.isTrue(value instanceof GeoJson<?>, "value of a GEO_CONTAINS filter must be a GeoJson object");
filter = geoJsonQuery(fieldName, (GeoJson<?>) value, "contains");
}
}
return filter;
}
private QueryBuilder withinQuery(String fieldName, Object... valArray) {
GeoDistanceQueryBuilder filter = QueryBuilders.geoDistanceQuery(fieldName);
Assert.noNullElements(valArray, "Geo distance filter takes 2 not null elements array as parameter.");
Assert.isTrue(valArray.length == 2, "Geo distance filter takes a 2-elements array as parameter.");
Assert.isTrue(valArray[0] instanceof GeoPoint || valArray[0] instanceof String || valArray[0] instanceof Point,
"First element of a geo distance filter must be a GeoPoint, a Point or a text");
Assert.isTrue(valArray[1] instanceof String || valArray[1] instanceof Distance,
"Second element of a geo distance filter must be a text or a Distance");
StringBuilder dist = new StringBuilder();
if (valArray[1] instanceof Distance) {
extractDistanceString((Distance) valArray[1], dist);
} else {
dist.append((String) valArray[1]);
}
if (valArray[0]instanceof GeoPoint loc) {
filter.point(loc.getLat(), loc.getLon()).distance(dist.toString()).geoDistance(GeoDistance.PLANE);
} else if (valArray[0] instanceof Point) {
GeoPoint loc = GeoPoint.fromPoint((Point) valArray[0]);
filter.point(loc.getLat(), loc.getLon()).distance(dist.toString()).geoDistance(GeoDistance.PLANE);
} else {
String loc = (String) valArray[0];
if (loc.contains(",")) {
String[] c = loc.split(",");
filter.point(Double.parseDouble(c[0]), Double.parseDouble(c[1])).distance(dist.toString())
.geoDistance(GeoDistance.PLANE);
} else {
filter.geohash(loc).distance(dist.toString()).geoDistance(GeoDistance.PLANE);
}
}
return filter;
}
private QueryBuilder boundingBoxQuery(String fieldName, Object... valArray) {
Assert.noNullElements(valArray, "Geo boundedBy filter takes a not null element array as parameter.");
GeoBoundingBoxQueryBuilder filter = QueryBuilders.geoBoundingBoxQuery(fieldName);
if (valArray.length == 1) {
// GeoEnvelop
oneParameterBBox(filter, valArray[0]);
} else if (valArray.length == 2) {
// 2x GeoPoint
// 2x text
twoParameterBBox(filter, valArray);
} else {
throw new IllegalArgumentException(
"Geo distance filter takes a 1-elements array(GeoBox) or 2-elements array(GeoPoints or Strings(format lat,lon or geohash)).");
}
return filter;
}
private QueryBuilder geoJsonQuery(String fieldName, GeoJson<?> geoJson, String relation) {
return QueryBuilders.wrapperQuery(buildJsonQuery(fieldName, geoJson, relation));
}
private String buildJsonQuery(String fieldName, GeoJson<?> geoJson, String relation) {
return "{\"geo_shape\": {\"" + fieldName + "\": {\"shape\": " + geoJson.toJson() + ", \"relation\": \"" + relation
+ "\"}}}";
}
/**
* extract the distance string from a {@link org.springframework.data.geo.Distance} object.
*
* @param distance distance object to extract string from
* @param sb StringBuilder to build the distance string
*/
private void extractDistanceString(Distance distance, StringBuilder sb) {
// handle Distance object
sb.append((int) distance.getValue());
Metrics metric = (Metrics) distance.getMetric();
switch (metric) {
case KILOMETERS -> sb.append("km");
case MILES -> sb.append("mi");
}
}
private void oneParameterBBox(GeoBoundingBoxQueryBuilder filter, Object value) {
Assert.isTrue(value instanceof GeoBox || value instanceof Box,
"single-element of boundedBy filter must be type of GeoBox or Box");
GeoBox geoBBox;
if (value instanceof Box) {
geoBBox = GeoBox.fromBox((Box) value);
} else {
geoBBox = (GeoBox) value;
}
filter.setCorners(geoBBox.getTopLeft().getLat(), geoBBox.getTopLeft().getLon(), geoBBox.getBottomRight().getLat(),
geoBBox.getBottomRight().getLon());
}
private static boolean isType(Object[] array, Class<?> clazz) {
for (Object o : array) {
if (!clazz.isInstance(o)) {
return false;
}
}
return true;
}
private void twoParameterBBox(GeoBoundingBoxQueryBuilder filter, Object... values) {
Assert.isTrue(isType(values, GeoPoint.class) || isType(values, String.class),
" both elements of boundedBy filter must be type of GeoPoint or text(format lat,lon or geohash)");
if (values[0]instanceof GeoPoint topLeft) {
GeoPoint bottomRight = (GeoPoint) values[1];
filter.setCorners(topLeft.getLat(), topLeft.getLon(), bottomRight.getLat(), bottomRight.getLon());
} else {
String topLeft = (String) values[0];
String bottomRight = (String) values[1];
filter.setCorners(topLeft, bottomRight);
}
}
private List<QueryBuilder> buildNegationFilter(String fieldName, Iterator<Criteria.CriteriaEntry> it) {
List<QueryBuilder> notFilterList = new LinkedList<>();
while (it.hasNext()) {
Criteria.CriteriaEntry criteriaEntry = it.next();
QueryBuilder notFilter = QueryBuilders.boolQuery()
.mustNot(queryFor(criteriaEntry.getKey(), criteriaEntry.getValue(), fieldName));
notFilterList.add(notFilter);
}
return notFilterList;
}
}

View File

@ -1,294 +0,0 @@
/*
* Copyright 2013-2023 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.erhlc;
import static org.elasticsearch.index.query.Operator.*;
import static org.elasticsearch.index.query.QueryBuilders.*;
import static org.springframework.data.elasticsearch.core.query.Criteria.*;
import static org.springframework.util.StringUtils.*;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.apache.lucene.queryparser.flexible.standard.QueryParserUtil;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.springframework.data.elasticsearch.annotations.FieldType;
import org.springframework.data.elasticsearch.core.query.Criteria;
import org.springframework.data.elasticsearch.core.query.Field;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
* CriteriaQueryProcessor
*
* @author Rizwan Idrees
* @author Mohsin Husen
* @author Franck Marchand
* @author Artur Konczak
* @author Rasmus Faber-Espensen
* @author James Bodkin
* @author Peter-Josef Meisch
* @author Ezequiel Antúnez Camacho
* @deprecated since 5.0
*/
@Deprecated
class CriteriaQueryProcessor {
@Nullable
QueryBuilder createQuery(Criteria criteria) {
Assert.notNull(criteria, "criteria must not be null");
List<QueryBuilder> shouldQueryBuilders = new ArrayList<>();
List<QueryBuilder> mustNotQueryBuilders = new ArrayList<>();
List<QueryBuilder> mustQueryBuilders = new ArrayList<>();
QueryBuilder firstQuery = null;
boolean negateFirstQuery = false;
for (Criteria chainedCriteria : criteria.getCriteriaChain()) {
QueryBuilder queryFragment = queryForEntries(chainedCriteria);
if (queryFragment != null) {
if (firstQuery == null) {
firstQuery = queryFragment;
negateFirstQuery = chainedCriteria.isNegating();
continue;
}
if (chainedCriteria.isOr()) {
shouldQueryBuilders.add(queryFragment);
} else if (chainedCriteria.isNegating()) {
mustNotQueryBuilders.add(queryFragment);
} else {
mustQueryBuilders.add(queryFragment);
}
}
}
for (Criteria subCriteria : criteria.getSubCriteria()) {
QueryBuilder subQuery = createQuery(subCriteria);
if (subQuery != null) {
if (criteria.isOr()) {
shouldQueryBuilders.add(subQuery);
} else if (criteria.isNegating()) {
mustNotQueryBuilders.add(subQuery);
} else {
mustQueryBuilders.add(subQuery);
}
}
}
if (firstQuery != null) {
if (!shouldQueryBuilders.isEmpty() && mustNotQueryBuilders.isEmpty() && mustQueryBuilders.isEmpty()) {
shouldQueryBuilders.add(0, firstQuery);
} else {
if (negateFirstQuery) {
mustNotQueryBuilders.add(0, firstQuery);
} else {
mustQueryBuilders.add(0, firstQuery);
}
}
}
BoolQueryBuilder query = null;
if (!shouldQueryBuilders.isEmpty() || !mustNotQueryBuilders.isEmpty() || !mustQueryBuilders.isEmpty()) {
query = boolQuery();
for (QueryBuilder qb : shouldQueryBuilders) {
query.should(qb);
}
for (QueryBuilder qb : mustNotQueryBuilders) {
query.mustNot(qb);
}
for (QueryBuilder qb : mustQueryBuilders) {
query.must(qb);
}
}
return query;
}
@Nullable
private QueryBuilder queryForEntries(Criteria criteria) {
Field field = criteria.getField();
if (field == null || criteria.getQueryCriteriaEntries().isEmpty())
return null;
String fieldName = field.getName();
Assert.notNull(fieldName, "Unknown field " + fieldName);
Iterator<Criteria.CriteriaEntry> it = criteria.getQueryCriteriaEntries().iterator();
QueryBuilder query;
if (criteria.getQueryCriteriaEntries().size() == 1) {
query = queryFor(it.next(), field);
} else {
query = boolQuery();
while (it.hasNext()) {
Criteria.CriteriaEntry entry = it.next();
((BoolQueryBuilder) query).must(queryFor(entry, field));
}
}
addBoost(query, criteria.getBoost());
if (hasText(field.getPath())) {
query = nestedQuery(field.getPath(), query, ScoreMode.Avg);
}
return query;
}
@Nullable
private QueryBuilder queryFor(Criteria.CriteriaEntry entry, Field field) {
QueryBuilder query = null;
String fieldName = field.getName();
boolean isKeywordField = FieldType.Keyword == field.getFieldType();
OperationKey key = entry.getKey();
// operations without a value
switch (key) {
case EXISTS -> query = existsQuery(fieldName);
case EMPTY -> query = boolQuery().must(existsQuery(fieldName)).mustNot(wildcardQuery(fieldName, "*"));
case NOT_EMPTY -> query = wildcardQuery(fieldName, "*");
default -> {}
}
if (query != null) {
return query;
}
// now operation keys with a value
Object value = entry.getValue();
String searchText = QueryParserUtil.escape(value.toString());
switch (key) {
case EQUALS:
query = queryStringQuery(searchText).field(fieldName).defaultOperator(AND);
break;
case CONTAINS:
query = queryStringQuery('*' + searchText + '*').field(fieldName).analyzeWildcard(true);
break;
case STARTS_WITH:
query = queryStringQuery(searchText + '*').field(fieldName).analyzeWildcard(true);
break;
case ENDS_WITH:
query = queryStringQuery('*' + searchText).field(fieldName).analyzeWildcard(true);
break;
case EXPRESSION:
query = queryStringQuery(value.toString()).field(fieldName);
break;
case LESS_EQUAL:
query = rangeQuery(fieldName).lte(value);
break;
case GREATER_EQUAL:
query = rangeQuery(fieldName).gte(value);
break;
case BETWEEN:
Object[] ranges = (Object[]) value;
query = rangeQuery(fieldName).from(ranges[0]).to(ranges[1]);
break;
case LESS:
query = rangeQuery(fieldName).lt(value);
break;
case GREATER:
query = rangeQuery(fieldName).gt(value);
break;
case FUZZY:
query = fuzzyQuery(fieldName, searchText);
break;
case MATCHES:
query = matchQuery(fieldName, value).operator(org.elasticsearch.index.query.Operator.OR);
break;
case MATCHES_ALL:
query = matchQuery(fieldName, value).operator(org.elasticsearch.index.query.Operator.AND);
break;
case IN:
if (value instanceof Iterable<?> iterable) {
if (isKeywordField) {
query = boolQuery().must(termsQuery(fieldName, toStringList(iterable)));
} else {
query = queryStringQuery(orQueryString(iterable)).field(fieldName);
}
}
break;
case NOT_IN:
if (value instanceof Iterable<?> iterable) {
if (isKeywordField) {
query = boolQuery().mustNot(termsQuery(fieldName, toStringList(iterable)));
} else {
query = queryStringQuery("NOT(" + orQueryString(iterable) + ')').field(fieldName);
}
}
break;
case REGEXP:
query = regexpQuery(fieldName, value.toString());
break;
}
return query;
}
private static List<String> toStringList(Iterable<?> iterable) {
List<String> list = new ArrayList<>();
for (Object item : iterable) {
list.add(item != null ? item.toString() : null);
}
return list;
}
private static String orQueryString(Iterable<?> iterable) {
StringBuilder sb = new StringBuilder();
for (Object item : iterable) {
if (item != null) {
if (sb.length() > 0) {
sb.append(' ');
}
sb.append('"');
sb.append(QueryParserUtil.escape(item.toString()));
sb.append('"');
}
}
return sb.toString();
}
private void addBoost(@Nullable QueryBuilder query, float boost) {
if (query == null || Float.isNaN(boost)) {
return;
}
query.boost(boost);
}
}

View File

@ -1,47 +0,0 @@
/*
* Copyright 2021-2023 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.erhlc;
import org.elasticsearch.action.admin.cluster.health.ClusterHealthRequest;
import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse;
import org.elasticsearch.client.RequestOptions;
import org.springframework.data.elasticsearch.core.cluster.ClusterHealth;
import org.springframework.data.elasticsearch.core.cluster.ClusterOperations;
/**
* Default implementation of {@link ClusterOperations} using the {@link ElasticsearchRestTemplate}.
*
* @author Peter-Josef Meisch
* @since 4.2
* @deprecated since 5.0
*/
@Deprecated
class DefaultClusterOperations implements ClusterOperations {
private final ElasticsearchRestTemplate template;
DefaultClusterOperations(ElasticsearchRestTemplate template) {
this.template = template;
}
@Override
public ClusterHealth health() {
ClusterHealthResponse clusterHealthResponse = template
.execute(client -> client.cluster().health(new ClusterHealthRequest(), RequestOptions.DEFAULT));
return ResponseConverter.clusterHealth(clusterHealthResponse);
}
}

View File

@ -1,45 +0,0 @@
/*
* Copyright 2021-2023 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.erhlc;
import reactor.core.publisher.Mono;
import org.elasticsearch.action.admin.cluster.health.ClusterHealthRequest;
import org.springframework.data.elasticsearch.core.ReactiveElasticsearchOperations;
import org.springframework.data.elasticsearch.core.cluster.ClusterHealth;
import org.springframework.data.elasticsearch.core.cluster.ReactiveClusterOperations;
/**
* Default implementation of {@link ReactiveClusterOperations} using the {@link ReactiveElasticsearchOperations}.
*
* @author Peter-Josef Meisch
* @since 4.2
* @deprecated since 5.0
*/
@Deprecated
public class DefaultReactiveClusterOperations implements ReactiveClusterOperations {
private final ReactiveElasticsearchOperations operations;
public DefaultReactiveClusterOperations(ReactiveElasticsearchOperations operations) {
this.operations = operations;
}
@Override
public Mono<ClusterHealth> health() {
return Mono.from(operations.executeWithClusterClient(
client -> client.health(new ClusterHealthRequest()).map(ResponseConverter::clusterHealth)));
}
}

View File

@ -1,907 +0,0 @@
/*
* Copyright 2018-2023 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.erhlc;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.ConnectException;
import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
import java.util.Map.Entry;
import java.util.function.Function;
import java.util.function.Supplier;
import org.apache.http.util.EntityUtils;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.ElasticsearchStatusException;
import org.elasticsearch.action.admin.cluster.health.ClusterHealthRequest;
import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse;
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
import org.elasticsearch.action.admin.indices.alias.get.GetAliasesRequest;
import org.elasticsearch.action.admin.indices.close.CloseIndexRequest;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.admin.indices.flush.FlushRequest;
import org.elasticsearch.action.admin.indices.flush.FlushResponse;
import org.elasticsearch.action.admin.indices.open.OpenIndexRequest;
import org.elasticsearch.action.admin.indices.refresh.RefreshRequest;
import org.elasticsearch.action.admin.indices.refresh.RefreshResponse;
import org.elasticsearch.action.admin.indices.settings.get.GetSettingsRequest;
import org.elasticsearch.action.admin.indices.settings.get.GetSettingsResponse;
import org.elasticsearch.action.admin.indices.template.delete.DeleteIndexTemplateRequest;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.get.MultiGetItemResponse;
import org.elasticsearch.action.get.MultiGetRequest;
import org.elasticsearch.action.get.MultiGetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.main.MainRequest;
import org.elasticsearch.action.main.MainResponse;
import org.elasticsearch.action.search.ClearScrollRequest;
import org.elasticsearch.action.search.ClearScrollResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.search.SearchScrollRequest;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.GetAliasesResponse;
import org.elasticsearch.client.Request;
import org.elasticsearch.client.indices.*;
import org.elasticsearch.client.tasks.TaskSubmissionResponse;
import org.elasticsearch.core.TimeValue;
import org.elasticsearch.index.get.GetResult;
import org.elasticsearch.index.reindex.BulkByScrollResponse;
import org.elasticsearch.index.reindex.DeleteByQueryRequest;
import org.elasticsearch.index.reindex.ReindexRequest;
import org.elasticsearch.index.reindex.UpdateByQueryRequest;
import org.elasticsearch.rest.BytesRestResponse;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.script.mustache.SearchTemplateRequest;
import org.elasticsearch.script.mustache.SearchTemplateResponse;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.suggest.Suggest;
import org.elasticsearch.xcontent.DeprecationHandler;
import org.elasticsearch.xcontent.NamedXContentRegistry;
import org.elasticsearch.xcontent.XContentParser;
import org.elasticsearch.xcontent.XContentType;
import org.reactivestreams.Publisher;
import org.springframework.data.elasticsearch.RestStatusException;
import org.springframework.data.elasticsearch.UncategorizedElasticsearchException;
import org.springframework.data.elasticsearch.client.ClientConfiguration;
import org.springframework.data.elasticsearch.client.ClientLogger;
import org.springframework.data.elasticsearch.client.ElasticsearchHost;
import org.springframework.data.elasticsearch.client.NoReachableHostException;
import org.springframework.data.elasticsearch.client.erhlc.HostProvider.Verification;
import org.springframework.data.elasticsearch.client.erhlc.ReactiveElasticsearchClient.Cluster;
import org.springframework.data.elasticsearch.client.erhlc.ReactiveElasticsearchClient.Indices;
import org.springframework.data.elasticsearch.client.util.ScrollState;
import org.springframework.data.elasticsearch.core.query.ByQueryResponse;
import org.springframework.data.util.Lazy;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.reactive.function.BodyExtractors;
import org.springframework.web.reactive.function.client.ClientRequest;
import org.springframework.web.reactive.function.client.ClientResponse;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.reactive.function.client.WebClient.RequestBodySpec;
/**
* A {@link WebClient} based {@link ReactiveElasticsearchClient} that connects to an Elasticsearch cluster using HTTP.
*
* @author Christoph Strobl
* @author Mark Paluch
* @author Peter-Josef Meisch
* @author Huw Ayling-Miller
* @author Henrique Amaral
* @author Roman Puchkovskiy
* @author Russell Parry
* @author Thomas Geese
* @author Brian Clozel
* @author Farid Faoudi
* @author George Popides
* @author Sijia Liu
* @since 3.2
* @see ClientConfiguration
* @see ReactiveRestClients
* @deprecated since 5.0
*/
@Deprecated
public class DefaultReactiveElasticsearchClient implements ReactiveElasticsearchClient, Indices, Cluster {
private final HostProvider<?> hostProvider;
private final RequestCreator requestCreator;
private Supplier<org.springframework.data.elasticsearch.support.HttpHeaders> headersSupplier = org.springframework.data.elasticsearch.support.HttpHeaders::new;
/**
* Create a new {@link DefaultReactiveElasticsearchClient} using the given {@link HostProvider} to obtain server
* connections.
*
* @param hostProvider must not be {@literal null}.
*/
public DefaultReactiveElasticsearchClient(HostProvider<?> hostProvider) {
this(hostProvider, new DefaultRequestCreator());
}
/**
* Create a new {@link DefaultReactiveElasticsearchClient} using the given {@link HostProvider} to obtain server
* connections and the given {@link RequestCreator}.
*
* @param hostProvider must not be {@literal null}.
* @param requestCreator must not be {@literal null}.
*/
public DefaultReactiveElasticsearchClient(HostProvider<?> hostProvider, RequestCreator requestCreator) {
Assert.notNull(hostProvider, "HostProvider must not be null");
Assert.notNull(requestCreator, "RequestCreator must not be null");
this.hostProvider = hostProvider;
this.requestCreator = requestCreator;
}
/**
* Create a new {@link DefaultReactiveElasticsearchClient} aware of the given nodes in the cluster. <br />
* <strong>NOTE</strong> If the cluster requires authentication be sure to provide the according {@link HttpHeaders}
* correctly.
*
* @param headers Use {@link HttpHeaders} to provide eg. authentication data. Must not be {@literal null}.
* @param hosts must not be {@literal null} nor empty!
* @return new instance of {@link DefaultReactiveElasticsearchClient}.
*/
public static ReactiveElasticsearchClient create(HttpHeaders headers, String... hosts) {
Assert.notNull(headers, "HttpHeaders must not be null");
Assert.notEmpty(hosts, "Elasticsearch Cluster needs to consist of at least one host");
var httpHeaders = new org.springframework.data.elasticsearch.support.HttpHeaders();
httpHeaders.addAll(headers);
ClientConfiguration clientConfiguration = ClientConfiguration.builder().connectedTo(hosts)
.withDefaultHeaders(httpHeaders).build();
return create(clientConfiguration);
}
/**
* Create a new {@link DefaultReactiveElasticsearchClient} given {@link ClientConfiguration}. <br />
* <strong>NOTE</strong> If the cluster requires authentication be sure to provide the according {@link HttpHeaders}
* correctly.
*
* @param clientConfiguration Client configuration. Must not be {@literal null}.
* @return new instance of {@link DefaultReactiveElasticsearchClient}.
*/
public static ReactiveElasticsearchClient create(ClientConfiguration clientConfiguration) {
return create(clientConfiguration, new DefaultRequestCreator());
}
/**
* Create a new {@link DefaultReactiveElasticsearchClient} given {@link ClientConfiguration} and
* {@link RequestCreator}. <br />
* <strong>NOTE</strong> If the cluster requires authentication be sure to provide the according {@link HttpHeaders}
* correctly.
*
* @param clientConfiguration Client configuration. Must not be {@literal null}.
* @param requestCreator Request creator. Must not be {@literal null}.
* @return new instance of {@link DefaultReactiveElasticsearchClient}.
*/
public static ReactiveElasticsearchClient create(ClientConfiguration clientConfiguration,
RequestCreator requestCreator) {
Assert.notNull(clientConfiguration, "ClientConfiguration must not be null");
Assert.notNull(requestCreator, "RequestCreator must not be null");
WebClientProvider provider = WebClientProvider.getWebClientProvider(clientConfiguration);
HostProvider<?> hostProvider = HostProvider.provider(provider, clientConfiguration.getHeadersSupplier(),
clientConfiguration.getEndpoints().toArray(new InetSocketAddress[0]));
DefaultReactiveElasticsearchClient client = new DefaultReactiveElasticsearchClient(hostProvider, requestCreator);
client.setHeadersSupplier(clientConfiguration.getHeadersSupplier());
return client;
}
public void setHeadersSupplier(Supplier<org.springframework.data.elasticsearch.support.HttpHeaders> headersSupplier) {
Assert.notNull(headersSupplier, "headersSupplier must not be null");
this.headersSupplier = headersSupplier;
}
@Override
public Mono<Boolean> ping(HttpHeaders headers) {
return sendRequest(new MainRequest(), requestCreator.ping(), RawActionResponse.class, headers) //
.flatMap(response -> response.releaseBody().thenReturn(response.statusCode().is2xxSuccessful())) //
.onErrorResume(NoReachableHostException.class, error -> Mono.just(false)).next();
}
@Override
public Mono<MainResponse> info(HttpHeaders headers) {
return sendRequest(new MainRequest(), requestCreator.info(), MainResponse.class, headers) //
.next();
}
@Override
public Mono<GetResult> get(HttpHeaders headers, GetRequest getRequest) {
return sendRequest(getRequest, requestCreator.get(), GetResponse.class, headers) //
.filter(GetResponse::isExists) //
.map(DefaultReactiveElasticsearchClient::getResponseToGetResult) //
.next();
}
@Override
public Flux<MultiGetItemResponse> multiGet(HttpHeaders headers, MultiGetRequest multiGetRequest) {
return sendRequest(multiGetRequest, requestCreator.multiGet(), MultiGetResponse.class, headers)
.map(MultiGetResponse::getResponses) //
.flatMap(Flux::fromArray); //
}
@Override
public Mono<Boolean> exists(HttpHeaders headers, GetRequest getRequest) {
return sendRequest(getRequest, requestCreator.exists(), RawActionResponse.class, headers) //
.flatMap(response -> response.releaseBody().thenReturn(response.statusCode().is2xxSuccessful())) //
.next();
}
@Override
public Mono<IndexResponse> index(HttpHeaders headers, IndexRequest indexRequest) {
return sendRequest(indexRequest, requestCreator.index(), IndexResponse.class, headers).next();
}
@Override
public Indices indices() {
return this;
}
@Override
public Cluster cluster() {
return this;
}
@Override
public Mono<UpdateResponse> update(HttpHeaders headers, UpdateRequest updateRequest) {
return sendRequest(updateRequest, requestCreator.update(), UpdateResponse.class, headers).next();
}
@Override
public Mono<DeleteResponse> delete(HttpHeaders headers, DeleteRequest deleteRequest) {
return sendRequest(deleteRequest, requestCreator.delete(), DeleteResponse.class, headers) //
.next();
}
@Override
public Mono<Long> count(HttpHeaders headers, SearchRequest searchRequest) {
searchRequest.source().trackTotalHits(true);
searchRequest.source().size(0);
searchRequest.source().fetchSource(false);
return sendRequest(searchRequest, requestCreator.search(), SearchResponse.class, headers) //
.map(SearchResponse::getHits) //
.map(searchHits -> searchHits.getTotalHits().value) //
.next();
}
@Override
public Flux<SearchHit> searchTemplate(HttpHeaders headers, SearchTemplateRequest searchTemplateRequest) {
return sendRequest(searchTemplateRequest, requestCreator.searchTemplate(), SearchTemplateResponse.class, headers)
.map(response -> response.getResponse().getHits()).flatMap(Flux::fromIterable);
}
@Override
public Flux<SearchHit> search(HttpHeaders headers, SearchRequest searchRequest) {
return sendRequest(searchRequest, requestCreator.search(), SearchResponse.class, headers) //
.map(SearchResponse::getHits) //
.flatMap(Flux::fromIterable);
}
@Override
public Mono<SearchResponse> searchForResponse(HttpHeaders headers, SearchRequest searchRequest) {
return sendRequest(searchRequest, requestCreator.search(), SearchResponse.class, headers).next();
}
@Override
public Flux<Suggest> suggest(HttpHeaders headers, SearchRequest searchRequest) {
return sendRequest(searchRequest, requestCreator.search(), SearchResponse.class, headers) //
.map(SearchResponse::getSuggest);
}
@Override
public Flux<Aggregation> aggregate(HttpHeaders headers, SearchRequest searchRequest) {
Assert.notNull(headers, "headers must not be null");
Assert.notNull(searchRequest, "searchRequest must not be null");
searchRequest.source().size(0);
searchRequest.source().trackTotalHits(false);
return sendRequest(searchRequest, requestCreator.search(), SearchResponse.class, headers) //
.map(SearchResponse::getAggregations) //
.flatMap(Flux::fromIterable);
}
@Override
public Flux<SearchHit> scroll(HttpHeaders headers, SearchRequest searchRequest) {
TimeValue scrollTimeout = searchRequest.scroll() != null ? searchRequest.scroll().keepAlive()
: TimeValue.timeValueMinutes(1);
if (searchRequest.scroll() == null) {
searchRequest.scroll(scrollTimeout);
}
return Flux.usingWhen(Mono.fromSupplier(ScrollState::new),
state -> sendRequest(searchRequest, requestCreator.search(), SearchResponse.class, headers)
.expand(searchResponse -> {
state.updateScrollId(searchResponse.getScrollId());
if (isEmpty(searchResponse.getHits())) {
return Mono.empty();
}
return sendRequest(new SearchScrollRequest(searchResponse.getScrollId()).scroll(scrollTimeout),
requestCreator.scroll(), SearchResponse.class, headers);
}),
state -> cleanupScroll(headers, state), //
(state, ex) -> cleanupScroll(headers, state), //
state -> cleanupScroll(headers, state)) //
.filter(it -> !isEmpty(it.getHits())) //
.map(SearchResponse::getHits) //
.flatMapIterable(Function.identity()); //
}
private static boolean isEmpty(@Nullable SearchHits hits) {
return hits != null && hits.getHits() != null && hits.getHits().length == 0;
}
private Publisher<?> cleanupScroll(HttpHeaders headers, ScrollState state) {
if (state.getScrollIds().isEmpty()) {
return Mono.empty();
}
ClearScrollRequest clearScrollRequest = new ClearScrollRequest();
clearScrollRequest.scrollIds(state.getScrollIds());
// just send the request, resources get cleaned up anyways after scrollTimeout has been reached.
return sendRequest(clearScrollRequest, requestCreator.clearScroll(), ClearScrollResponse.class, headers);
}
@Override
public Mono<BulkByScrollResponse> deleteBy(HttpHeaders headers, DeleteByQueryRequest deleteRequest) {
return sendRequest(deleteRequest, requestCreator.deleteByQuery(), BulkByScrollResponse.class, headers) //
.next();
}
@Override
public Mono<ByQueryResponse> updateBy(HttpHeaders headers, UpdateByQueryRequest updateRequest) {
return sendRequest(updateRequest, requestCreator.updateByQuery(), BulkByScrollResponse.class, headers) //
.next() //
.map(ResponseConverter::byQueryResponseOf);
}
@Override
public Mono<BulkResponse> bulk(HttpHeaders headers, BulkRequest bulkRequest) {
return sendRequest(bulkRequest, requestCreator.bulk(), BulkResponse.class, headers) //
.next();
}
@Override
public Mono<BulkByScrollResponse> reindex(HttpHeaders headers, ReindexRequest reindexRequest) {
return sendRequest(reindexRequest, requestCreator.reindex(), BulkByScrollResponse.class, headers).next();
}
@Override
public Mono<String> submitReindex(HttpHeaders headers, ReindexRequest reindexRequest) {
return sendRequest(reindexRequest, requestCreator.submitReindex(), TaskSubmissionResponse.class, headers).next()
.map(TaskSubmissionResponse::getTask);
}
@Override
public <T> Mono<T> execute(ReactiveElasticsearchClientCallback<T> callback) {
return this.hostProvider.getActive(Verification.LAZY) //
.flatMap(callback::doWithClient) //
.onErrorResume(throwable -> {
if (isCausedByConnectionException(throwable)) {
return hostProvider.getActive(Verification.ACTIVE) //
.flatMap(callback::doWithClient);
}
return Mono.error(throwable);
});
}
/**
* checks if the given throwable is a {@link ConnectException} or has one in it's cause chain
*
* @param throwable the throwable to check
* @return true if throwable is caused by a {@link ConnectException}
*/
private boolean isCausedByConnectionException(Throwable throwable) {
Throwable t = throwable;
do {
if (t instanceof ConnectException) {
return true;
}
t = t.getCause();
} while (t != null);
return false;
}
@Override
public Mono<Status> status() {
return hostProvider.clusterInfo() //
.map(it -> new ClientStatus(it.getNodes()));
}
// --> Private Response helpers
private static GetResult getResponseToGetResult(GetResponse response) {
return new GetResult(response.getIndex(), response.getType(), response.getId(), response.getSeqNo(),
response.getPrimaryTerm(), response.getVersion(), response.isExists(), response.getSourceAsBytesRef(),
response.getFields(), null);
}
// -->
private <REQ, RESP> Flux<RESP> sendRequest(REQ request, Function<REQ, Request> converter, Class<RESP> responseType,
HttpHeaders headers) {
return sendRequest(converter.apply(request), responseType, headers);
}
private <Resp> Flux<Resp> sendRequest(Request request, Class<Resp> responseType, HttpHeaders headers) {
String logId = ClientLogger.newLogId();
return Flux
.from(execute(webClient -> sendRequest(webClient, logId, request, headers).exchangeToMono(clientResponse -> {
Publisher<? extends Resp> publisher = readResponseBody(logId, request, clientResponse, responseType);
return Mono.from(publisher);
})));
}
private RequestBodySpec sendRequest(WebClient webClient, String logId, Request request, HttpHeaders headers) {
RequestBodySpec requestBodySpec = webClient.method(HttpMethod.valueOf(request.getMethod().toUpperCase())) //
.uri(builder -> {
builder = builder.path(request.getEndpoint());
if (!ObjectUtils.isEmpty(request.getParameters())) {
for (Entry<String, String> entry : request.getParameters().entrySet()) {
builder = builder.queryParam(entry.getKey(), entry.getValue());
}
}
return builder.build();
}) //
.attribute(ClientRequest.LOG_ID_ATTRIBUTE, logId) //
.headers(theHeaders -> {
// add all the headers explicitly set
theHeaders.addAll(headers);
// and now those that might be set on the request.
if (request.getOptions() != null) {
if (!ObjectUtils.isEmpty(request.getOptions().getHeaders())) {
request.getOptions().getHeaders().forEach(it -> theHeaders.add(it.getName(), it.getValue()));
}
}
});
if (request.getEntity() != null) {
Lazy<String> body = bodyExtractor(request);
ClientLogger.logRequest(logId, request.getMethod().toUpperCase(), request.getEndpoint(), request.getParameters(),
body::get);
requestBodySpec.contentType(MediaType.valueOf(request.getEntity().getContentType().getValue()))
.body(Mono.fromSupplier(body), String.class);
} else {
ClientLogger.logRequest(logId, request.getMethod().toUpperCase(), request.getEndpoint(), request.getParameters());
}
return requestBodySpec;
}
// region indices operations
@Override
public Mono<Boolean> createIndex(HttpHeaders headers,
org.elasticsearch.action.admin.indices.create.CreateIndexRequest createIndexRequest) {
return sendRequest(createIndexRequest, requestCreator.indexCreate(), AcknowledgedResponse.class, headers) //
.map(AcknowledgedResponse::isAcknowledged) //
.next();
}
@Override
public Mono<Boolean> createIndex(HttpHeaders headers, CreateIndexRequest createIndexRequest) {
return sendRequest(createIndexRequest, requestCreator.createIndexRequest(), AcknowledgedResponse.class, headers) //
.map(AcknowledgedResponse::isAcknowledged) //
.next();
}
@Override
public Mono<Void> closeIndex(HttpHeaders headers, CloseIndexRequest closeIndexRequest) {
return sendRequest(closeIndexRequest, requestCreator.indexClose(), AcknowledgedResponse.class, headers) //
.then();
}
@Override
public Mono<Boolean> existsIndex(HttpHeaders headers,
org.elasticsearch.action.admin.indices.get.GetIndexRequest request) {
return sendRequest(request, requestCreator.indexExists(), RawActionResponse.class, headers) //
.flatMap(response -> response.releaseBody().thenReturn(response.statusCode().is2xxSuccessful())) //
.next();
}
@Override
public Mono<Boolean> existsIndex(HttpHeaders headers, GetIndexRequest request) {
return sendRequest(request, requestCreator.indexExistsRequest(), RawActionResponse.class, headers) //
.flatMap(response -> response.releaseBody().thenReturn(response.statusCode().is2xxSuccessful())) //
.next();
}
@Override
public Mono<Boolean> deleteIndex(HttpHeaders headers, DeleteIndexRequest request) {
return sendRequest(request, requestCreator.indexDelete(), AcknowledgedResponse.class, headers) //
.map(AcknowledgedResponse::isAcknowledged) //
.next();
}
@Override
public Mono<Void> flushIndex(HttpHeaders headers, FlushRequest flushRequest) {
return sendRequest(flushRequest, requestCreator.flushIndex(), FlushResponse.class, headers) //
.then();
}
@Override
@Deprecated
public Mono<org.elasticsearch.action.admin.indices.mapping.get.GetMappingsResponse> getMapping(HttpHeaders headers,
org.elasticsearch.action.admin.indices.mapping.get.GetMappingsRequest getMappingsRequest) {
return sendRequest(getMappingsRequest, requestCreator.getMapping(),
org.elasticsearch.action.admin.indices.mapping.get.GetMappingsResponse.class, headers).next();
}
@Override
public Mono<GetMappingsResponse> getMapping(HttpHeaders headers, GetMappingsRequest getMappingsRequest) {
return sendRequest(getMappingsRequest, requestCreator.getMappingRequest(), GetMappingsResponse.class, headers) //
.next();
}
@Override
public Mono<GetFieldMappingsResponse> getFieldMapping(HttpHeaders headers,
GetFieldMappingsRequest getFieldMappingsRequest) {
return sendRequest(getFieldMappingsRequest, requestCreator.getFieldMapping(), GetFieldMappingsResponse.class,
headers).next();
}
@Override
public Mono<GetSettingsResponse> getSettings(HttpHeaders headers, GetSettingsRequest getSettingsRequest) {
return sendRequest(getSettingsRequest, requestCreator.getSettings(), GetSettingsResponse.class, headers).next();
}
@Override
public Mono<Boolean> putMapping(HttpHeaders headers,
org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest putMappingRequest) {
return sendRequest(putMappingRequest, requestCreator.putMapping(), AcknowledgedResponse.class, headers) //
.map(AcknowledgedResponse::isAcknowledged) //
.next();
}
@Override
public Mono<Boolean> putMapping(HttpHeaders headers, PutMappingRequest putMappingRequest) {
return sendRequest(putMappingRequest, requestCreator.putMappingRequest(), AcknowledgedResponse.class, headers) //
.map(AcknowledgedResponse::isAcknowledged) //
.next();
}
@Override
public Mono<Void> openIndex(HttpHeaders headers, OpenIndexRequest request) {
return sendRequest(request, requestCreator.indexOpen(), AcknowledgedResponse.class, headers) //
.then();
}
@Override
public Mono<Void> refreshIndex(HttpHeaders headers, RefreshRequest refreshRequest) {
return sendRequest(refreshRequest, requestCreator.indexRefresh(), RefreshResponse.class, headers) //
.then();
}
@Override
public Mono<Boolean> updateAliases(HttpHeaders headers, IndicesAliasesRequest indicesAliasesRequest) {
return sendRequest(indicesAliasesRequest, requestCreator.updateAlias(), AcknowledgedResponse.class, headers)
.map(AcknowledgedResponse::isAcknowledged).next();
}
@Override
public Mono<GetAliasesResponse> getAliases(HttpHeaders headers, GetAliasesRequest getAliasesRequest) {
return sendRequest(getAliasesRequest, requestCreator.getAlias(), GetAliasesResponse.class, headers).next();
}
@Override
public Mono<Boolean> putTemplate(HttpHeaders headers, PutIndexTemplateRequest putIndexTemplateRequest) {
return sendRequest(putIndexTemplateRequest, requestCreator.putTemplate(), AcknowledgedResponse.class, headers)
.map(AcknowledgedResponse::isAcknowledged).next();
}
@Override
public Mono<GetIndexTemplatesResponse> getTemplate(HttpHeaders headers,
GetIndexTemplatesRequest getIndexTemplatesRequest) {
return (sendRequest(getIndexTemplatesRequest, requestCreator.getTemplates(), GetIndexTemplatesResponse.class,
headers)).next();
}
@Override
public Mono<Boolean> existsTemplate(HttpHeaders headers, IndexTemplatesExistRequest indexTemplatesExistRequest) {
return sendRequest(indexTemplatesExistRequest, requestCreator.templatesExist(), RawActionResponse.class, headers) //
.flatMap(response -> response.releaseBody().thenReturn(response.statusCode().is2xxSuccessful())) //
.next();
}
@Override
public Mono<Boolean> deleteTemplate(HttpHeaders headers, DeleteIndexTemplateRequest deleteIndexTemplateRequest) {
return sendRequest(deleteIndexTemplateRequest, requestCreator.deleteTemplate(), AcknowledgedResponse.class, headers)
.map(AcknowledgedResponse::isAcknowledged).next();
}
@Override
public Mono<GetIndexResponse> getIndex(HttpHeaders headers, GetIndexRequest getIndexRequest) {
return sendRequest(getIndexRequest, requestCreator.getIndex(), GetIndexResponse.class, headers).next();
}
// endregion
// region cluster operations
@Override
public Mono<ClusterHealthResponse> health(HttpHeaders headers, ClusterHealthRequest clusterHealthRequest) {
return sendRequest(clusterHealthRequest, requestCreator.clusterHealth(), ClusterHealthResponse.class, headers)
.next();
}
// endregion
// region helper functions
private <T> Publisher<? extends T> readResponseBody(String logId, Request request, ClientResponse response,
Class<T> responseType) {
if (RawActionResponse.class.equals(responseType)) {
ClientLogger.logRawResponse(logId, response.statusCode().value());
return Mono.just(responseType.cast(RawActionResponse.create(response)));
}
if (response.statusCode().is5xxServerError()) {
ClientLogger.logRawResponse(logId, response.statusCode().value());
return handleServerError(request, response);
}
if (response.statusCode().is4xxClientError()) {
ClientLogger.logRawResponse(logId, response.statusCode().value());
return handleClientError(logId, response, responseType);
}
return response.body(BodyExtractors.toMono(byte[].class)) //
.map(it -> new String(it, StandardCharsets.UTF_8)) //
.doOnNext(it -> ClientLogger.logResponse(logId, response.statusCode().value(), it)) //
.flatMap(content -> doDecode(response, responseType, content));
}
private static <T> Mono<T> doDecode(ClientResponse response, Class<T> responseType, String content) {
String mediaType = response.headers().contentType().map(MediaType::toString).orElse(XContentType.JSON.mediaType());
try {
Method fromXContent = ReflectionUtils.findMethod(responseType, "fromXContent", XContentParser.class);
if (fromXContent == null) {
return Mono.error(new UncategorizedElasticsearchException(
"No method named fromXContent found in " + responseType.getCanonicalName()));
}
return Mono.justOrEmpty(responseType
.cast(ReflectionUtils.invokeMethod(fromXContent, responseType, createParser(mediaType, content))));
} catch (Throwable errorParseFailure) { // cause elasticsearch also uses AssertionError
try {
return Mono.error(BytesRestResponse.errorFromXContent(createParser(mediaType, content)));
} catch (Exception e) {
return Mono.error(new RestStatusException(response.statusCode().value(), content));
}
}
}
private static XContentParser createParser(String mediaType, String content) throws IOException {
return XContentType.fromMediaTypeOrFormat(mediaType) //
.xContent() //
.createParser(new NamedXContentRegistry(NamedXContents.getDefaultNamedXContents()),
DeprecationHandler.THROW_UNSUPPORTED_OPERATION, content);
}
private Lazy<String> bodyExtractor(Request request) {
return Lazy.of(() -> {
try {
return EntityUtils.toString(request.getEntity());
} catch (IOException e) {
throw new RequestBodyEncodingException("Error encoding request", e);
}
});
}
// endregion
// region error and exception handling
private <T> Publisher<? extends T> handleServerError(Request request, ClientResponse response) {
int statusCode = response.statusCode().value();
RestStatus status = RestStatus.fromCode(statusCode);
String mediaType = response.headers().contentType().map(MediaType::toString).orElse(XContentType.JSON.mediaType());
return response.body(BodyExtractors.toMono(byte[].class)) //
.switchIfEmpty(Mono.error(new RestStatusException(status.getStatus(),
String.format("%s request to %s returned error code %s and no body.", request.getMethod(),
request.getEndpoint(), statusCode))))
.map(bytes -> new String(bytes, StandardCharsets.UTF_8)) //
.flatMap(content -> contentOrError(content, mediaType, status))
.flatMap(unused -> Mono
.error(new RestStatusException(status.getStatus(), String.format("%s request to %s returned error code %s.",
request.getMethod(), request.getEndpoint(), statusCode))));
}
private <T> Publisher<? extends T> handleClientError(String logId, ClientResponse response, Class<T> responseType) {
int statusCode = response.statusCode().value();
RestStatus status = RestStatus.fromCode(statusCode);
String mediaType = response.headers().contentType().map(MediaType::toString).orElse(XContentType.JSON.mediaType());
return response.body(BodyExtractors.toMono(byte[].class)) //
.map(bytes -> new String(bytes, StandardCharsets.UTF_8)) //
.flatMap(content -> contentOrError(content, mediaType, status)) //
.doOnNext(content -> ClientLogger.logResponse(logId, response.statusCode().value(), content)) //
.flatMap(content -> doDecode(response, responseType, content));
}
/**
* checks if the given content body contains an {@link ElasticsearchException}, if yes it is returned in a Mono.error.
* Otherwise the content is returned in the Mono
*
* @param content the content to analyze
* @param mediaType the returned media type
* @param status the response status
* @return a Mono with the content or an Mono.error
*/
private static Mono<String> contentOrError(String content, String mediaType, RestStatus status) {
ElasticsearchException exception = getElasticsearchException(content, mediaType, status);
if (exception != null) {
StringBuilder sb = new StringBuilder();
buildExceptionMessages(sb, exception);
return Mono.error(new RestStatusException(status.getStatus(), sb.toString(), exception));
}
return Mono.just(content);
}
/**
* tries to parse an {@link ElasticsearchException} from the given body content
*
* @param content the content to analyse
* @param mediaType the type of the body content
* @return an {@link ElasticsearchException} or {@literal null}.
*/
@Nullable
private static ElasticsearchException getElasticsearchException(String content, String mediaType, RestStatus status) {
try {
XContentParser parser = createParser(mediaType, content);
// we have a JSON object with an error and a status field
parser.nextToken(); // Skip START_OBJECT
XContentParser.Token token;
do {
token = parser.nextToken();
if ("error".equals(parser.currentName())) {
return ElasticsearchException.failureFromXContent(parser);
}
} while (token == XContentParser.Token.FIELD_NAME);
return null;
} catch (Exception e) {
return new ElasticsearchStatusException(content, status);
}
}
private static void buildExceptionMessages(StringBuilder sb, Throwable t) {
sb.append(t.getMessage());
for (Throwable throwable : t.getSuppressed()) {
sb.append(", ");
buildExceptionMessages(sb, throwable);
}
}
// endregion
// region internal classes
/**
* Reactive client {@link ReactiveElasticsearchClient.Status} implementation.
*
* @author Christoph Strobl
*/
static class ClientStatus implements Status {
private final Collection<ElasticsearchHost> connectedHosts;
ClientStatus(Collection<ElasticsearchHost> connectedHosts) {
this.connectedHosts = connectedHosts;
}
/*
* (non-Javadoc)
* @see org.springframework.data.elasticsearch.client.erhlc.ReactiveElasticsearchClient.Status#hosts()
*/
@Override
public Collection<ElasticsearchHost> hosts() {
return connectedHosts;
}
}
// endregion
}

View File

@ -1,24 +0,0 @@
/*
* Copyright 2021-2023 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.erhlc;
/**
* @author Roman Puchkovskiy
* @since 4.0
* @deprecated since 5.0
*/
@Deprecated
class DefaultRequestCreator implements RequestCreator {}

View File

@ -1,193 +0,0 @@
/*
* Copyright 2018-2023 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.erhlc;
import java.net.InetSocketAddress;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
import java.util.function.Function;
import org.springframework.http.HttpHeaders;
import org.springframework.http.client.reactive.ClientHttpConnector;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.reactive.function.client.WebClient.Builder;
import org.springframework.web.util.DefaultUriBuilderFactory;
/**
* Default {@link WebClientProvider} that uses cached {@link WebClient} instances per {@code hostAndPort}.
*
* @author Mark Paluch
* @author Christoph Strobl
* @author Huw Ayling-Miller
* @author Peter-Josef Meisch
* @since 3.2
* @deprecated since 5.0
*/
@Deprecated
class DefaultWebClientProvider implements WebClientProvider {
private final Map<InetSocketAddress, WebClient> cachedClients;
private final String scheme;
private final @Nullable ClientHttpConnector connector;
private final Consumer<Throwable> errorListener;
private final HttpHeaders headers;
private final @Nullable String pathPrefix;
private final Function<WebClient, WebClient> webClientConfigurer;
private final Consumer<WebClient.RequestHeadersSpec<?>> requestConfigurer;
/**
* Create new {@link DefaultWebClientProvider} with empty {@link HttpHeaders} and no-op {@literal error listener}.
*
* @param scheme must not be {@literal null}.
* @param connector can be {@literal null}.
*/
DefaultWebClientProvider(String scheme, @Nullable ClientHttpConnector connector) {
this(scheme, connector, e -> {}, HttpHeaders.EMPTY, null, Function.identity(), requestHeadersSpec -> {});
}
/**
* Create new {@link DefaultWebClientProvider} with empty {@link HttpHeaders} and no-op {@literal error listener}.
*
* @param scheme must not be {@literal null}.
* @param connector can be {@literal null}.
* @param errorListener must not be {@literal null}.
* @param headers must not be {@literal null}.
* @param pathPrefix can be {@literal null}.
* @param webClientConfigurer must not be {@literal null}.
* @param requestConfigurer must not be {@literal null}.
*/
private DefaultWebClientProvider(String scheme, @Nullable ClientHttpConnector connector,
Consumer<Throwable> errorListener, HttpHeaders headers, @Nullable String pathPrefix,
Function<WebClient, WebClient> webClientConfigurer, Consumer<WebClient.RequestHeadersSpec<?>> requestConfigurer) {
Assert.notNull(scheme, "Scheme must not be null! A common scheme would be 'http'.");
Assert.notNull(errorListener, "errorListener must not be null! You may want use a no-op one 'e -> {}' instead.");
Assert.notNull(headers, "headers must not be null! Think about using 'HttpHeaders.EMPTY' as an alternative.");
Assert.notNull(webClientConfigurer,
"webClientConfigurer must not be null! You may want use a no-op one 'Function.identity()' instead.");
Assert.notNull(requestConfigurer,
"requestConfigurer must not be null! You may want use a no-op one 'r -> {}' instead.\"");
this.cachedClients = new ConcurrentHashMap<>();
this.scheme = scheme;
this.connector = connector;
this.errorListener = errorListener;
this.headers = headers;
this.pathPrefix = pathPrefix;
this.webClientConfigurer = webClientConfigurer;
this.requestConfigurer = requestConfigurer;
}
@Override
public WebClient get(InetSocketAddress endpoint) {
Assert.notNull(endpoint, "Endpoint must not be empty!");
return this.cachedClients.computeIfAbsent(endpoint, this::createWebClientForSocketAddress);
}
@Override
public HttpHeaders getDefaultHeaders() {
return headers;
}
@Override
public Consumer<Throwable> getErrorListener() {
return this.errorListener;
}
@Nullable
@Override
public String getPathPrefix() {
return pathPrefix;
}
@Override
public WebClientProvider withDefaultHeaders(HttpHeaders headers) {
Assert.notNull(headers, "HttpHeaders must not be null.");
HttpHeaders merged = new HttpHeaders();
merged.addAll(this.headers);
merged.addAll(headers);
return new DefaultWebClientProvider(scheme, connector, errorListener, merged, pathPrefix, webClientConfigurer,
requestConfigurer);
}
@Override
public WebClientProvider withRequestConfigurer(Consumer<WebClient.RequestHeadersSpec<?>> requestConfigurer) {
Assert.notNull(requestConfigurer, "requestConfigurer must not be null.");
return new DefaultWebClientProvider(scheme, connector, errorListener, headers, pathPrefix, webClientConfigurer,
requestConfigurer);
}
@Override
public WebClientProvider withErrorListener(Consumer<Throwable> errorListener) {
Assert.notNull(errorListener, "Error listener must not be null.");
Consumer<Throwable> listener = this.errorListener.andThen(errorListener);
return new DefaultWebClientProvider(scheme, this.connector, listener, headers, pathPrefix, webClientConfigurer,
requestConfigurer);
}
@Override
public WebClientProvider withPathPrefix(String pathPrefix) {
Assert.notNull(pathPrefix, "pathPrefix must not be null.");
return new DefaultWebClientProvider(this.scheme, this.connector, this.errorListener, this.headers, pathPrefix,
webClientConfigurer, requestConfigurer);
}
@Override
public WebClientProvider withWebClientConfigurer(Function<WebClient, WebClient> webClientConfigurer) {
return new DefaultWebClientProvider(scheme, connector, errorListener, headers, pathPrefix, webClientConfigurer,
requestConfigurer);
}
protected WebClient createWebClientForSocketAddress(InetSocketAddress socketAddress) {
Builder builder = WebClient.builder() //
.defaultHeaders(it -> it.addAll(getDefaultHeaders())) //
.defaultRequest(requestConfigurer);
if (connector != null) {
builder = builder.clientConnector(connector);
}
String baseUrl = String.format("%s://%s:%d%s", this.scheme, socketAddress.getHostString(), socketAddress.getPort(),
pathPrefix == null ? "" : '/' + pathPrefix);
DefaultUriBuilderFactory uriBuilderFactory = new DefaultUriBuilderFactory(baseUrl);
// the template will already be encoded by the RequestConverters methods
uriBuilderFactory.setEncodingMode(DefaultUriBuilderFactory.EncodingMode.VALUES_ONLY);
builder.uriBuilderFactory(uriBuilderFactory); //
WebClient webClient = builder //
.filter((request, next) -> next.exchange(request) //
.doOnError(errorListener)) //
.build(); //
return webClientConfigurer.apply(webClient);
}
}

View File

@ -1,750 +0,0 @@
/*
* Copyright 2019-2023 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.erhlc;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.function.BiConsumer;
import java.util.stream.Collectors;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.get.MultiGetItemResponse;
import org.elasticsearch.action.get.MultiGetResponse;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.document.DocumentField;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.get.GetResult;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.springframework.data.elasticsearch.core.MultiGetItem;
import org.springframework.data.elasticsearch.core.document.Document;
import org.springframework.data.elasticsearch.core.document.Explanation;
import org.springframework.data.elasticsearch.core.document.NestedMetaData;
import org.springframework.data.elasticsearch.core.document.SearchDocument;
import org.springframework.data.elasticsearch.core.document.SearchDocumentResponse;
import org.springframework.data.mapping.MappingException;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import com.fasterxml.jackson.core.JsonEncoding;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;
/**
* Utility class to adapt {@link org.elasticsearch.action.get.GetResponse},
* {@link org.elasticsearch.index.get.GetResult}, {@link org.elasticsearch.action.get.MultiGetResponse}
* {@link org.elasticsearch.search.SearchHit}, {@link org.elasticsearch.common.document.DocumentField} to
* {@link Document}.
*
* @author Mark Paluch
* @author Peter-Josef Meisch
* @author Roman Puchkovskiy
* @author Matt Gilene
* @since 4.0
* @deprecated since 5.0
*/
@Deprecated
public final class DocumentAdapters {
private DocumentAdapters() {}
/**
* Create a {@link Document} from {@link GetResponse}.
* <p>
* Returns a {@link Document} using the getResponse if available.
*
* @param getResponse the getResponse {@link GetResponse}.
* @return the adapted {@link Document}, null if getResponse.isExists() returns false.
*/
@Nullable
public static Document from(GetResponse getResponse) {
Assert.notNull(getResponse, "GetResponse must not be null");
if (!getResponse.isExists()) {
return null;
}
if (getResponse.isSourceEmpty()) {
return fromDocumentFields(getResponse, getResponse.getIndex(), getResponse.getId(), getResponse.getVersion(),
getResponse.getSeqNo(), getResponse.getPrimaryTerm());
}
Document document = Document.from(getResponse.getSourceAsMap());
document.setIndex(getResponse.getIndex());
document.setId(getResponse.getId());
document.setVersion(getResponse.getVersion());
document.setSeqNo(getResponse.getSeqNo());
document.setPrimaryTerm(getResponse.getPrimaryTerm());
return document;
}
/**
* Create a {@link Document} from {@link GetResult}.
* <p>
* Returns a {@link Document} using the source if available.
*
* @param source the source {@link GetResult}.
* @return the adapted {@link Document}, null if source.isExists() returns false.
*/
@Nullable
public static Document from(GetResult source) {
Assert.notNull(source, "GetResult must not be null");
if (!source.isExists()) {
return null;
}
if (source.isSourceEmpty()) {
return fromDocumentFields(source, source.getIndex(), source.getId(), source.getVersion(), source.getSeqNo(),
source.getPrimaryTerm());
}
Document document = Document.from(source.getSource());
document.setIndex(source.getIndex());
document.setId(source.getId());
document.setVersion(source.getVersion());
document.setSeqNo(source.getSeqNo());
document.setPrimaryTerm(source.getPrimaryTerm());
return document;
}
/**
* Creates a List of {@link MultiGetItem<Document>}s from {@link MultiGetResponse}.
*
* @param source the source {@link MultiGetResponse}, not {@literal null}.
* @return a list of Documents, contains null values for not found Documents.
*/
public static List<MultiGetItem<Document>> from(MultiGetResponse source) {
Assert.notNull(source, "MultiGetResponse must not be null");
return Arrays.stream(source.getResponses()) //
.map(DocumentAdapters::from) //
.collect(Collectors.toList());
}
/**
* Creates a {@link MultiGetItem<Document>} from a {@link MultiGetItemResponse}.
*
* @param itemResponse the response, must not be {@literal null}
* @return the MultiGetItem
*/
public static MultiGetItem<Document> from(MultiGetItemResponse itemResponse) {
MultiGetItem.Failure failure = ResponseConverter.getFailure(itemResponse);
return MultiGetItem.of(itemResponse.isFailed() ? null : DocumentAdapters.from(itemResponse.getResponse()), failure);
}
/**
* Create a {@link SearchDocument} from {@link SearchHit}.
* <p>
* Returns a {@link SearchDocument} using the source if available.
*
* @param source the source {@link SearchHit}.
* @return the adapted {@link SearchDocument}.
*/
public static SearchDocument from(SearchHit source) {
Assert.notNull(source, "SearchHit must not be null");
Map<String, List<String>> highlightFields = new HashMap<>(source.getHighlightFields().entrySet().stream() //
.collect(Collectors.toMap(Map.Entry::getKey,
entry -> Arrays.stream(entry.getValue().getFragments()).map(Text::string).collect(Collectors.toList()))));
Map<String, SearchDocumentResponse> innerHits = new LinkedHashMap<>();
Map<String, SearchHits> sourceInnerHits = source.getInnerHits();
if (sourceInnerHits != null) {
sourceInnerHits.forEach((name, searchHits) -> innerHits.put(name, SearchDocumentResponseBuilder.from(searchHits,
null, null, null, searchDocument -> CompletableFuture.completedFuture(null))));
}
NestedMetaData nestedMetaData = from(source.getNestedIdentity());
Explanation explanation = from(source.getExplanation());
List<String> matchedQueries = from(source.getMatchedQueries());
BytesReference sourceRef = source.getSourceRef();
Map<String, DocumentField> sourceFields = source.getFields();
if (sourceRef == null || sourceRef.length() == 0) {
return new SearchDocumentAdapter(
fromDocumentFields(source, source.getIndex(), source.getId(), source.getVersion(), source.getSeqNo(),
source.getPrimaryTerm()),
source.getScore(), source.getSortValues(), sourceFields, highlightFields, innerHits, nestedMetaData,
explanation, matchedQueries);
}
Document document = Document.from(source.getSourceAsMap());
document.setIndex(source.getIndex());
document.setId(source.getId());
if (source.getVersion() >= 0) {
document.setVersion(source.getVersion());
}
document.setSeqNo(source.getSeqNo());
document.setPrimaryTerm(source.getPrimaryTerm());
return new SearchDocumentAdapter(document, source.getScore(), source.getSortValues(), sourceFields, highlightFields,
innerHits, nestedMetaData, explanation, matchedQueries);
}
@Nullable
private static Explanation from(@Nullable org.apache.lucene.search.Explanation explanation) {
if (explanation == null) {
return null;
}
List<Explanation> details = new ArrayList<>();
for (org.apache.lucene.search.Explanation detail : explanation.getDetails()) {
details.add(from(detail));
}
return new Explanation(explanation.isMatch(), explanation.getValue().doubleValue(), explanation.getDescription(),
details);
}
@Nullable
private static NestedMetaData from(@Nullable SearchHit.NestedIdentity nestedIdentity) {
if (nestedIdentity == null) {
return null;
}
NestedMetaData child = from(nestedIdentity.getChild());
return NestedMetaData.of(nestedIdentity.getField().string(), nestedIdentity.getOffset(), child);
}
@Nullable
private static List<String> from(@Nullable String[] matchedQueries) {
return matchedQueries == null ? null : Arrays.asList(matchedQueries);
}
/**
* Create an unmodifiable {@link Document} from {@link Iterable} of {@link DocumentField}s.
*
* @param documentFields the {@link DocumentField}s backing the {@link Document}.
* @param index the index where the Document was found
* @param id the document id
* @param version the document version
* @param seqNo the seqNo if the document
* @param primaryTerm the primaryTerm of the document
* @return the adapted {@link Document}.
*/
public static Document fromDocumentFields(Iterable<DocumentField> documentFields, String index, String id,
long version, long seqNo, long primaryTerm) {
if (documentFields instanceof Collection) {
return new DocumentFieldAdapter((Collection<DocumentField>) documentFields, index, id, version, seqNo,
primaryTerm);
}
List<DocumentField> fields = new ArrayList<>();
for (DocumentField documentField : documentFields) {
fields.add(documentField);
}
return new DocumentFieldAdapter(fields, index, id, version, seqNo, primaryTerm);
}
/**
* Adapter for a collection of {@link DocumentField}s.
*/
static class DocumentFieldAdapter implements Document {
private final Collection<DocumentField> documentFields;
private final Map<String, DocumentField> documentFieldMap;
private final String index;
private final String id;
private final long version;
private final long seqNo;
private final long primaryTerm;
DocumentFieldAdapter(Collection<DocumentField> documentFields, String index, String id, long version, long seqNo,
long primaryTerm) {
this.documentFields = documentFields;
this.documentFieldMap = new LinkedHashMap<>(documentFields.size());
documentFields.forEach(documentField -> documentFieldMap.put(documentField.getName(), documentField));
this.index = index;
this.id = id;
this.version = version;
this.seqNo = seqNo;
this.primaryTerm = primaryTerm;
}
@Override
public String getIndex() {
return index;
}
@Override
public boolean hasId() {
return StringUtils.hasLength(id);
}
@Override
public String getId() {
return this.id;
}
@Override
public boolean hasVersion() {
return this.version >= 0;
}
@Override
public long getVersion() {
if (!hasVersion()) {
throw new IllegalStateException("No version associated with this Document");
}
return this.version;
}
@Override
public boolean hasSeqNo() {
return true;
}
@Override
public long getSeqNo() {
if (!hasSeqNo()) {
throw new IllegalStateException("No seq_no associated with this Document");
}
return this.seqNo;
}
@Override
public boolean hasPrimaryTerm() {
return true;
}
@Override
public long getPrimaryTerm() {
if (!hasPrimaryTerm()) {
throw new IllegalStateException("No primary_term associated with this Document");
}
return this.primaryTerm;
}
@Override
public int size() {
return documentFields.size();
}
@Override
public boolean isEmpty() {
return documentFields.isEmpty();
}
@Override
public boolean containsKey(Object key) {
return documentFieldMap.containsKey(key);
}
@Override
public boolean containsValue(Object value) {
for (DocumentField documentField : documentFields) {
Object fieldValue = getValue(documentField);
if (fieldValue != null && fieldValue.equals(value) || value == fieldValue) {
return true;
}
}
return false;
}
@Override
@Nullable
public Object get(Object key) {
DocumentField documentField = documentFieldMap.get(key);
return documentField != null ? documentField.getValue() : null;
}
@Override
public Object put(String key, Object value) {
throw new UnsupportedOperationException();
}
@Override
public Object remove(Object key) {
throw new UnsupportedOperationException();
}
@Override
public void putAll(Map<? extends String, ?> m) {
throw new UnsupportedOperationException();
}
@Override
public void clear() {
throw new UnsupportedOperationException();
}
@Override
public Set<String> keySet() {
return documentFieldMap.keySet();
}
@Override
public Collection<Object> values() {
return documentFieldMap.values().stream().map(DocumentFieldAdapter::getValue).collect(Collectors.toList());
}
@Override
public Set<Entry<String, Object>> entrySet() {
return documentFieldMap.entrySet().stream()
.collect(Collectors.toMap(Entry::getKey, entry -> DocumentFieldAdapter.getValue(entry.getValue())))
.entrySet();
}
@Override
public void forEach(BiConsumer<? super String, ? super Object> action) {
Objects.requireNonNull(action);
documentFields.forEach(field -> action.accept(field.getName(), getValue(field)));
}
@Override
public String toJson() {
JsonFactory nodeFactory = new JsonFactory();
try {
ByteArrayOutputStream stream = new ByteArrayOutputStream();
JsonGenerator generator = nodeFactory.createGenerator(stream, JsonEncoding.UTF8);
generator.writeStartObject();
for (DocumentField value : documentFields) {
if (value.getValues().size() > 1) {
generator.writeArrayFieldStart(value.getName());
for (Object val : value.getValues()) {
generator.writeObject(val);
}
generator.writeEndArray();
} else {
generator.writeObjectField(value.getName(), value.getValue());
}
}
generator.writeEndObject();
generator.flush();
return new String(stream.toByteArray(), StandardCharsets.UTF_8);
} catch (IOException e) {
throw new MappingException("Cannot render JSON", e);
}
}
@Override
public String toString() {
return getClass().getSimpleName() + '@' + this.id + '#' + this.version + ' ' + toJson();
}
@Nullable
private static Object getValue(DocumentField documentField) {
if (documentField.getValues().isEmpty()) {
return null;
}
if (documentField.getValues().size() == 1) {
return documentField.getValue();
}
return documentField.getValues();
}
}
/**
* Adapter for a {@link SearchDocument}.
*/
static class SearchDocumentAdapter implements SearchDocument {
private final float score;
private final Object[] sortValues;
private final Map<String, List<Object>> fields = new HashMap<>();
private final Document delegate;
private final Map<String, List<String>> highlightFields = new HashMap<>();
private final Map<String, SearchDocumentResponse> innerHits = new HashMap<>();
@Nullable private final NestedMetaData nestedMetaData;
@Nullable private final Explanation explanation;
@Nullable private final List<String> matchedQueries;
SearchDocumentAdapter(Document delegate, float score, Object[] sortValues, Map<String, DocumentField> fields,
Map<String, List<String>> highlightFields, Map<String, SearchDocumentResponse> innerHits,
@Nullable NestedMetaData nestedMetaData, @Nullable Explanation explanation,
@Nullable List<String> matchedQueries) {
this.delegate = delegate;
this.score = score;
this.sortValues = sortValues;
fields.forEach((name, documentField) -> this.fields.put(name, documentField.getValues()));
this.highlightFields.putAll(highlightFields);
this.innerHits.putAll(innerHits);
this.nestedMetaData = nestedMetaData;
this.explanation = explanation;
this.matchedQueries = matchedQueries;
}
@Override
public SearchDocument append(String key, Object value) {
delegate.append(key, value);
return this;
}
@Override
public float getScore() {
return score;
}
@Override
public Map<String, List<Object>> getFields() {
return fields;
}
@Override
public Object[] getSortValues() {
return sortValues;
}
@Override
public Map<String, List<String>> getHighlightFields() {
return highlightFields;
}
@Override
public String getIndex() {
return delegate.getIndex();
}
@Override
public boolean hasId() {
return delegate.hasId();
}
@Override
public String getId() {
return delegate.getId();
}
@Override
public void setId(String id) {
delegate.setId(id);
}
@Override
public boolean hasVersion() {
return delegate.hasVersion();
}
@Override
public long getVersion() {
return delegate.getVersion();
}
@Override
public void setVersion(long version) {
delegate.setVersion(version);
}
@Override
public boolean hasSeqNo() {
return delegate.hasSeqNo();
}
@Override
public long getSeqNo() {
return delegate.getSeqNo();
}
@Override
public void setSeqNo(long seqNo) {
delegate.setSeqNo(seqNo);
}
@Override
public boolean hasPrimaryTerm() {
return delegate.hasPrimaryTerm();
}
@Override
public long getPrimaryTerm() {
return delegate.getPrimaryTerm();
}
@Override
public void setPrimaryTerm(long primaryTerm) {
delegate.setPrimaryTerm(primaryTerm);
}
@Override
public Map<String, SearchDocumentResponse> getInnerHits() {
return innerHits;
}
@Override
@Nullable
public NestedMetaData getNestedMetaData() {
return nestedMetaData;
}
@Override
@Nullable
public <T> T get(Object key, Class<T> type) {
return delegate.get(key, type);
}
@Override
public String toJson() {
return delegate.toJson();
}
@Override
public int size() {
return delegate.size();
}
@Override
public boolean isEmpty() {
return delegate.isEmpty();
}
@Override
public boolean containsKey(Object key) {
return delegate.containsKey(key);
}
@Override
public boolean containsValue(Object value) {
return delegate.containsValue(value);
}
@Override
public Object get(Object key) {
if (delegate.containsKey(key)) {
return delegate.get(key);
}
// fallback to fields
return fields.get(key);
}
@Override
public Object put(String key, Object value) {
return delegate.put(key, value);
}
@Override
public Object remove(Object key) {
return delegate.remove(key);
}
@Override
public void putAll(Map<? extends String, ?> m) {
delegate.putAll(m);
}
@Override
public void clear() {
delegate.clear();
}
@Override
public Set<String> keySet() {
return delegate.keySet();
}
@Override
public Collection<Object> values() {
return delegate.values();
}
@Override
public Set<Entry<String, Object>> entrySet() {
return delegate.entrySet();
}
@Override
@Nullable
public Explanation getExplanation() {
return explanation;
}
@Override
@Nullable
public List<String> getMatchedQueries() {
return matchedQueries;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof SearchDocumentAdapter that)) {
return false;
}
return Float.compare(that.score, score) == 0 && delegate.equals(that.delegate);
}
@Override
public int hashCode() {
return delegate.hashCode();
}
@Override
public void forEach(BiConsumer<? super String, ? super Object> action) {
delegate.forEach(action);
}
@Override
public boolean remove(Object key, Object value) {
return delegate.remove(key, value);
}
@Override
public String toString() {
String id = hasId() ? getId() : "?";
String version = hasVersion() ? Long.toString(getVersion()) : "?";
return getClass().getSimpleName() + '@' + id + '#' + version + ' ' + toJson();
}
}
}

View File

@ -1,43 +0,0 @@
/*
* Copyright 2021-2023 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.erhlc;
import org.elasticsearch.search.aggregations.Aggregation;
import org.springframework.data.elasticsearch.core.AggregationContainer;
import org.springframework.lang.NonNull;
/**
* AggregationContainer implementation for an Elasticsearch7 aggregation.
*
* @author Peter-Josef Meisch
* @since 4.3
* @deprecated since 5.0
*/
@Deprecated
public class ElasticsearchAggregation implements AggregationContainer<Aggregation> {
private final Aggregation aggregation;
public ElasticsearchAggregation(Aggregation aggregation) {
this.aggregation = aggregation;
}
@NonNull
@Override
public Aggregation aggregation() {
return aggregation;
}
}

View File

@ -1,43 +0,0 @@
/*
* Copyright 2021-2023 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.erhlc;
import org.elasticsearch.search.aggregations.Aggregations;
import org.springframework.data.elasticsearch.core.AggregationsContainer;
import org.springframework.lang.NonNull;
/**
* AggregationsContainer implementation for the Elasticsearch7 aggregations.
*
* @author Peter-Josef Meisch
* @since 4.3
* @deprecated since 5.0
*/
@Deprecated
public class ElasticsearchAggregations implements AggregationsContainer<Aggregations> {
private final Aggregations aggregations;
public ElasticsearchAggregations(Aggregations aggregations) {
this.aggregations = aggregations;
}
@NonNull
@Override
public Aggregations aggregations() {
return aggregations;
}
}

View File

@ -1,41 +0,0 @@
/*
* Copyright 2021-2023 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.erhlc;
import org.springframework.data.elasticsearch.core.cluster.ClusterOperations;
import org.springframework.util.Assert;
/**
* @author Peter-Josef Meisch
* @since 4.4
* @deprecated since 5.0
*/
@Deprecated
public class ElasticsearchClusterOperations {
/**
* Creates a ClusterOperations for a {@link ElasticsearchRestTemplate}.
*
* @param template the template, must not be {@literal null}
* @return ClusterOperations
*/
public static ClusterOperations forTemplate(ElasticsearchRestTemplate template) {
Assert.notNull(template, "template must not be null");
return new DefaultClusterOperations(template);
}
}

View File

@ -1,144 +0,0 @@
/*
* Copyright 2018-2023 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.erhlc;
import java.io.IOException;
import java.util.List;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.ElasticsearchStatusException;
import org.elasticsearch.common.ValidationException;
import org.elasticsearch.index.engine.VersionConflictEngineException;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.DataAccessResourceFailureException;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.dao.OptimisticLockingFailureException;
import org.springframework.dao.support.PersistenceExceptionTranslator;
import org.springframework.data.elasticsearch.NoSuchIndexException;
import org.springframework.data.elasticsearch.RestStatusException;
import org.springframework.data.elasticsearch.UncategorizedElasticsearchException;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
/**
* Simple {@link PersistenceExceptionTranslator} for Elasticsearch. Convert the given runtime exception to an
* appropriate exception from the {@code org.springframework.dao} hierarchy. Return {@literal null} if no translation is
* appropriate: any other exception may have resulted from user code, and should not be translated.
*
* @author Christoph Strobl
* @author Peter-Josef Meisch
* @author Roman Puchkovskiy
* @author Mark Paluch
* @since 3.2
* @deprecated since 5.0
*/
@Deprecated
public class ElasticsearchExceptionTranslator implements PersistenceExceptionTranslator {
@Override
public DataAccessException translateExceptionIfPossible(RuntimeException ex) {
if (isSeqNoConflict(ex)) {
return new OptimisticLockingFailureException("Cannot index a document due to seq_no+primary_term conflict", ex);
}
if (ex instanceof ElasticsearchException elasticsearchException) {
if (!indexAvailable(elasticsearchException)) {
return new NoSuchIndexException(ObjectUtils.nullSafeToString(elasticsearchException.getMetadata("es.index")),
ex);
}
if (elasticsearchException instanceof ElasticsearchStatusException elasticsearchStatusException) {
return new RestStatusException(elasticsearchStatusException.status().getStatus(),
elasticsearchStatusException.getMessage(), elasticsearchStatusException);
}
return new UncategorizedElasticsearchException(ex.getMessage(), ex);
}
if (ex instanceof RestStatusException restStatusException) {
Throwable cause = restStatusException.getCause();
if (cause instanceof ElasticsearchException elasticsearchException) {
if (!indexAvailable(elasticsearchException)) {
return new NoSuchIndexException(ObjectUtils.nullSafeToString(elasticsearchException.getMetadata("es.index")),
ex);
}
}
}
if (ex instanceof ValidationException) {
return new DataIntegrityViolationException(ex.getMessage(), ex);
}
Throwable cause = ex.getCause();
if (cause instanceof IOException) {
return new DataAccessResourceFailureException(ex.getMessage(), ex);
}
return null;
}
private boolean isSeqNoConflict(Exception exception) {
Integer status = null;
String message = null;
if (exception instanceof ElasticsearchStatusException statusException) {
status = statusException.status().getStatus();
message = statusException.getMessage();
}
if (exception instanceof RestStatusException statusException) {
status = statusException.getStatus();
message = statusException.getMessage();
}
if (status != null && message != null) {
return status == 409 && message.contains("type=version_conflict_engine_exception")
&& message.contains("version conflict, required seqNo");
}
if (exception instanceof VersionConflictEngineException versionConflictEngineException) {
return versionConflictEngineException.getMessage() != null
&& versionConflictEngineException.getMessage().contains("version conflict, required seqNo");
}
return false;
}
private boolean indexAvailable(ElasticsearchException ex) {
List<String> metadata = ex.getMetadata("es.index_uuid");
if (metadata == null) {
if (ex.getCause() instanceof ElasticsearchException) {
return indexAvailable((ElasticsearchException) ex.getCause());
}
if (ex instanceof ElasticsearchStatusException) {
return StringUtils.hasText(ObjectUtils.nullSafeToString(ex.getIndex()));
}
return true;
}
return !CollectionUtils.contains(metadata.iterator(), "_na_");
}
}

View File

@ -1,698 +0,0 @@
/*
* Copyright 2013-2023 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.erhlc;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.elasticsearch.Version;
import org.elasticsearch.action.DocWriteResponse;
import org.elasticsearch.action.bulk.BulkItemResponse;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.get.MultiGetRequest;
import org.elasticsearch.action.get.MultiGetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.ClearScrollRequest;
import org.elasticsearch.action.search.MultiSearchRequest;
import org.elasticsearch.action.search.MultiSearchResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.search.SearchScrollRequest;
import org.elasticsearch.action.support.WriteRequest;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.core.TimeValue;
import org.elasticsearch.index.query.MoreLikeThisQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.reindex.BulkByScrollResponse;
import org.elasticsearch.index.reindex.DeleteByQueryRequest;
import org.elasticsearch.index.reindex.UpdateByQueryRequest;
import org.elasticsearch.search.fetch.subphase.FetchSourceContext;
import org.elasticsearch.search.suggest.SuggestBuilder;
import org.springframework.data.elasticsearch.BulkFailureException;
import org.springframework.data.elasticsearch.core.AbstractElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.IndexOperations;
import org.springframework.data.elasticsearch.core.IndexedObjectInformation;
import org.springframework.data.elasticsearch.core.MultiGetItem;
import org.springframework.data.elasticsearch.core.RefreshPolicy;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.SearchScrollHits;
import org.springframework.data.elasticsearch.core.cluster.ClusterOperations;
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.BulkOptions;
import org.springframework.data.elasticsearch.core.query.ByQueryResponse;
import org.springframework.data.elasticsearch.core.query.IndexQuery;
import org.springframework.data.elasticsearch.core.query.MoreLikeThisQuery;
import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.data.elasticsearch.core.query.UpdateQuery;
import org.springframework.data.elasticsearch.core.query.UpdateResponse;
import org.springframework.data.elasticsearch.core.reindex.ReindexRequest;
import org.springframework.data.elasticsearch.core.reindex.ReindexResponse;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
* ElasticsearchRestTemplate
*
* @author Rizwan Idrees
* @author Mohsin Husen
* @author Artur Konczak
* @author Kevin Leturc
* @author Mason Chan
* @author Young Gu
* @author Oliver Gierke
* @author Mark Janssen
* @author Chris White
* @author Mark Paluch
* @author Ilkang Na
* @author Alen Turkovic
* @author Sascha Woo
* @author Ted Liang
* @author Don Wellington
* @author Zetang Zeng
* @author Peter Nowak
* @author Ivan Greene
* @author Christoph Strobl
* @author Lorenzo Spinelli
* @author Dmitriy Yakovlev
* @author Roman Puchkovskiy
* @author Martin Choraine
* @author Farid Azaza
* @author Peter-Josef Meisch
* @author Mathias Teier
* @author Gyula Attila Csorogi
* @author Massimiliano Poggi
* @author Farid Faoudi
* @author Sijia Liu
* @author Hamid Rahimi
* @since 4.4
* @deprecated since 5.0
*/
@Deprecated
public class ElasticsearchRestTemplate extends AbstractElasticsearchTemplate {
private static final Log LOGGER = LogFactory.getLog(ElasticsearchRestTemplate.class);
private final RestHighLevelClient client;
private final ElasticsearchExceptionTranslator exceptionTranslator = new ElasticsearchExceptionTranslator();
protected RequestFactory requestFactory;
// region _initialization
public ElasticsearchRestTemplate(RestHighLevelClient client) {
Assert.notNull(client, "Client must not be null!");
this.client = client;
requestFactory = new RequestFactory(this.elasticsearchConverter);
}
public ElasticsearchRestTemplate(RestHighLevelClient client, ElasticsearchConverter elasticsearchConverter) {
super(elasticsearchConverter);
Assert.notNull(client, "Client must not be null!");
this.client = client;
requestFactory = new RequestFactory(this.elasticsearchConverter);
}
@Override
protected AbstractElasticsearchTemplate doCopy() {
ElasticsearchRestTemplate copy = new ElasticsearchRestTemplate(client, elasticsearchConverter);
copy.requestFactory = this.requestFactory;
return copy;
}
/**
* @since 4.0
*/
public RequestFactory getRequestFactory() {
return requestFactory;
}
// endregion
// region IndexOperations
@Override
public IndexOperations indexOps(Class<?> clazz) {
Assert.notNull(clazz, "clazz must not be null");
return new RestIndexTemplate(this, clazz);
}
@Override
public IndexOperations indexOps(IndexCoordinates index) {
Assert.notNull(index, "index must not be null");
return new RestIndexTemplate(this, index);
}
// endregion
// region ClusterOperations
@Override
public ClusterOperations cluster() {
return ElasticsearchClusterOperations.forTemplate(this);
}
// endregion
// region DocumentOperations
public String doIndex(IndexQuery query, IndexCoordinates index) {
IndexRequest request = prepareWriteRequest(requestFactory.indexRequest(query, index));
IndexResponse indexResponse = execute(client -> client.index(request, RequestOptions.DEFAULT));
Object queryObject = query.getObject();
if (queryObject != null) {
query.setObject(updateIndexedObject(queryObject, new IndexedObjectInformation( //
indexResponse.getId(), //
indexResponse.getIndex(), //
indexResponse.getSeqNo(), //
indexResponse.getPrimaryTerm(), //
indexResponse.getVersion())));
}
return indexResponse.getId();
}
@Override
@Nullable
public <T> T get(String id, Class<T> clazz, IndexCoordinates index) {
GetRequest request = requestFactory.getRequest(id, routingResolver.getRouting(), index);
GetResponse response = execute(client -> client.get(request, RequestOptions.DEFAULT));
DocumentCallback<T> callback = new ReadDocumentCallback<>(elasticsearchConverter, clazz, index);
return callback.doWith(DocumentAdapters.from(response));
}
@Override
public <T> List<MultiGetItem<T>> multiGet(Query query, Class<T> clazz, IndexCoordinates index) {
Assert.notNull(index, "index must not be null");
MultiGetRequest request = requestFactory.multiGetRequest(query, clazz, index);
MultiGetResponse result = execute(client -> client.mget(request, RequestOptions.DEFAULT));
DocumentCallback<T> callback = new ReadDocumentCallback<>(elasticsearchConverter, clazz, index);
return DocumentAdapters.from(result).stream() //
.map(multiGetItem -> MultiGetItem.of( //
multiGetItem.isFailed() ? null : callback.doWith(multiGetItem.getItem()), multiGetItem.getFailure())) //
.collect(Collectors.toList());
}
@Override
protected boolean doExists(String id, IndexCoordinates index) {
GetRequest request = requestFactory.getRequest(id, routingResolver.getRouting(), index);
request.fetchSourceContext(FetchSourceContext.DO_NOT_FETCH_SOURCE);
return execute(client -> client.get(request, RequestOptions.DEFAULT).isExists());
}
@Override
public void bulkUpdate(List<UpdateQuery> queries, BulkOptions bulkOptions, IndexCoordinates index) {
Assert.notNull(queries, "List of UpdateQuery must not be null");
Assert.notNull(bulkOptions, "BulkOptions must not be null");
doBulkOperation(queries, bulkOptions, index);
}
@Override
protected String doDelete(String id, @Nullable String routing, IndexCoordinates index) {
Assert.notNull(id, "id must not be null");
Assert.notNull(index, "index must not be null");
DeleteRequest request = prepareWriteRequest(
requestFactory.deleteRequest(elasticsearchConverter.convertId(id), routing, index));
return execute(client -> client.delete(request, RequestOptions.DEFAULT).getId());
}
@Override
public ByQueryResponse delete(Query query, Class<?> clazz, IndexCoordinates index) {
DeleteByQueryRequest deleteByQueryRequest = requestFactory.deleteByQueryRequest(query, routingResolver.getRouting(),
clazz, index);
return ResponseConverter
.byQueryResponseOf(execute(client -> client.deleteByQuery(deleteByQueryRequest, RequestOptions.DEFAULT)));
}
@Override
public UpdateResponse update(UpdateQuery query, IndexCoordinates index) {
UpdateRequest request = requestFactory.updateRequest(query, index);
if (query.getRefreshPolicy() == null && getRefreshPolicy() != null) {
request.setRefreshPolicy(RequestFactory.toElasticsearchRefreshPolicy(getRefreshPolicy()));
}
if (query.getRouting() == null && routingResolver.getRouting() != null) {
request.routing(routingResolver.getRouting());
}
UpdateResponse.Result result = UpdateResponse.Result
.valueOf(execute(client -> client.update(request, RequestOptions.DEFAULT)).getResult().name());
return new UpdateResponse(result);
}
@Override
public ByQueryResponse updateByQuery(UpdateQuery query, IndexCoordinates index) {
Assert.notNull(query, "query must not be null");
Assert.notNull(index, "index must not be null");
UpdateByQueryRequest updateByQueryRequest = requestFactory.updateByQueryRequest(query, index);
if (query.getRefreshPolicy() == null && getRefreshPolicy() != null) {
updateByQueryRequest.setRefresh(getRefreshPolicy() == RefreshPolicy.IMMEDIATE);
}
if (query.getRouting() == null && routingResolver.getRouting() != null) {
updateByQueryRequest.setRouting(routingResolver.getRouting());
}
final BulkByScrollResponse bulkByScrollResponse = execute(
client -> client.updateByQuery(updateByQueryRequest, RequestOptions.DEFAULT));
return ResponseConverter.byQueryResponseOf(bulkByScrollResponse);
}
@Override
public ReindexResponse reindex(ReindexRequest reindexRequest) {
Assert.notNull(reindexRequest, "reindexRequest must not be null");
org.elasticsearch.index.reindex.ReindexRequest reindexRequestES = requestFactory.reindexRequest(reindexRequest);
BulkByScrollResponse bulkByScrollResponse = execute(
client -> client.reindex(reindexRequestES, RequestOptions.DEFAULT));
return ResponseConverter.reindexResponseOf(bulkByScrollResponse);
}
@Override
public String submitReindex(ReindexRequest reindexRequest) {
Assert.notNull(reindexRequest, "reindexRequest must not be null");
org.elasticsearch.index.reindex.ReindexRequest reindexRequestES = requestFactory.reindexRequest(reindexRequest);
return execute(client -> client.submitReindexTask(reindexRequestES, RequestOptions.DEFAULT).getTask());
}
public List<IndexedObjectInformation> doBulkOperation(List<?> queries, BulkOptions bulkOptions,
IndexCoordinates index) {
BulkRequest bulkRequest = prepareWriteRequest(requestFactory.bulkRequest(queries, bulkOptions, index));
List<IndexedObjectInformation> indexedObjectInformationList = checkForBulkOperationFailure(
execute(client -> client.bulk(bulkRequest, RequestOptions.DEFAULT)));
updateIndexedObjectsWithQueries(queries, indexedObjectInformationList);
return indexedObjectInformationList;
}
/**
* Preprocess the write request before it is sent to the server, e.g. by setting the
* {@link WriteRequest#setRefreshPolicy(String) refresh policy} if applicable.
*
* @param request must not be {@literal null}.
* @param <R> the request type
* @return the processed {@link WriteRequest}.
*/
protected <R extends WriteRequest<R>> R prepareWriteRequest(R request) {
if (refreshPolicy == null) {
return request;
}
return request.setRefreshPolicy(RequestFactory.toElasticsearchRefreshPolicy(refreshPolicy));
}
/**
* extract the list of {@link IndexedObjectInformation} from a {@link BulkResponse}.
*
* @param bulkResponse the response to evaluate
* @return the list of the {@link IndexedObjectInformation}s
*/
protected List<IndexedObjectInformation> checkForBulkOperationFailure(BulkResponse bulkResponse) {
if (bulkResponse.hasFailures()) {
Map<String, String> failedDocuments = new HashMap<>();
for (BulkItemResponse item : bulkResponse.getItems()) {
if (item.isFailed())
failedDocuments.put(item.getId(), item.getFailureMessage());
}
throw new BulkFailureException(
"Bulk operation has failures. Use BulkFailureException.getFailedDocuments() for detailed messages ["
+ failedDocuments + ']',
failedDocuments);
}
return Stream.of(bulkResponse.getItems()).map(bulkItemResponse -> {
DocWriteResponse response = bulkItemResponse.getResponse();
if (response != null) {
return new IndexedObjectInformation( //
response.getId(), //
response.getIndex(), //
response.getSeqNo(), //
response.getPrimaryTerm(), //
response.getVersion());
} else {
return new IndexedObjectInformation(bulkItemResponse.getId(), bulkItemResponse.getIndex(), null, null, null);
}
}).collect(Collectors.toList());
}
// endregion
// region SearchOperations
@Override
public long count(Query query, @Nullable Class<?> clazz, IndexCoordinates index) {
Assert.notNull(query, "query must not be null");
Assert.notNull(index, "index must not be null");
final Boolean trackTotalHits = query.getTrackTotalHits();
query.setTrackTotalHits(true);
SearchRequest searchRequest = requestFactory.searchRequest(query, routingResolver.getRouting(), clazz, index);
query.setTrackTotalHits(trackTotalHits);
searchRequest.source().size(0);
return SearchHitsUtil
.getTotalCount(execute(client -> client.search(searchRequest, RequestOptions.DEFAULT).getHits()));
}
@Override
public <T> SearchHits<T> search(Query query, Class<T> clazz, IndexCoordinates index) {
SearchRequest searchRequest = requestFactory.searchRequest(query, routingResolver.getRouting(), clazz, index);
SearchResponse response = execute(client -> client.search(searchRequest, RequestOptions.DEFAULT));
ReadDocumentCallback<T> documentCallback = new ReadDocumentCallback<>(elasticsearchConverter, clazz, index);
SearchDocumentResponseCallback<SearchHits<T>> callback = new ReadSearchDocumentResponseCallback<>(clazz, index);
return callback.doWith(SearchDocumentResponseBuilder.from(response, getEntityCreator(documentCallback)));
}
protected <T> SearchHits<T> doSearch(MoreLikeThisQuery query, Class<T> clazz, IndexCoordinates index) {
MoreLikeThisQueryBuilder moreLikeThisQueryBuilder = requestFactory.moreLikeThisQueryBuilder(query, index);
return search(
new NativeSearchQueryBuilder().withQuery(moreLikeThisQueryBuilder).withPageable(query.getPageable()).build(),
clazz, index);
}
@Override
public <T> SearchScrollHits<T> searchScrollStart(long scrollTimeInMillis, Query query, Class<T> clazz,
IndexCoordinates index) {
Assert.notNull(query.getPageable(), "pageable of query must not be null.");
SearchRequest searchRequest = requestFactory.searchRequest(query, routingResolver.getRouting(), clazz, index);
searchRequest.scroll(TimeValue.timeValueMillis(scrollTimeInMillis));
SearchResponse response = execute(client -> client.search(searchRequest, RequestOptions.DEFAULT));
ReadDocumentCallback<T> documentCallback = new ReadDocumentCallback<>(elasticsearchConverter, clazz, index);
SearchDocumentResponseCallback<SearchScrollHits<T>> callback = new ReadSearchScrollDocumentResponseCallback<>(clazz,
index);
return callback.doWith(SearchDocumentResponseBuilder.from(response, getEntityCreator(documentCallback)));
}
@Override
public <T> SearchScrollHits<T> searchScrollContinue(String scrollId, long scrollTimeInMillis, Class<T> clazz,
IndexCoordinates index) {
SearchScrollRequest request = new SearchScrollRequest(scrollId);
request.scroll(TimeValue.timeValueMillis(scrollTimeInMillis));
SearchResponse response = execute(client -> client.scroll(request, RequestOptions.DEFAULT));
ReadDocumentCallback<T> documentCallback = new ReadDocumentCallback<>(elasticsearchConverter, clazz, index);
SearchDocumentResponseCallback<SearchScrollHits<T>> callback = new ReadSearchScrollDocumentResponseCallback<>(clazz,
index);
return callback.doWith(SearchDocumentResponseBuilder.from(response, getEntityCreator(documentCallback)));
}
@Override
public void searchScrollClear(List<String> scrollIds) {
try {
ClearScrollRequest request = new ClearScrollRequest();
request.scrollIds(scrollIds);
execute(client -> client.clearScroll(request, RequestOptions.DEFAULT));
} catch (Exception e) {
LOGGER.warn(String.format("Could not clear scroll: %s", e.getMessage()));
}
}
public SearchResponse suggest(SuggestBuilder suggestion, IndexCoordinates index) {
SearchRequest searchRequest = requestFactory.searchRequest(suggestion, index);
return execute(client -> client.search(searchRequest, RequestOptions.DEFAULT));
}
@Override
public <T> List<SearchHits<T>> multiSearch(List<? extends Query> queries, Class<T> clazz, IndexCoordinates index) {
MultiSearchRequest request = new MultiSearchRequest();
for (Query query : queries) {
request.add(requestFactory.searchRequest(query, routingResolver.getRouting(), clazz, index));
}
MultiSearchResponse.Item[] items = getMultiSearchResult(request);
ReadDocumentCallback<T> documentCallback = new ReadDocumentCallback<>(elasticsearchConverter, clazz, index);
SearchDocumentResponseCallback<SearchHits<T>> callback = new ReadSearchDocumentResponseCallback<>(clazz, index);
List<SearchHits<T>> res = new ArrayList<>(queries.size());
for (int i = 0; i < queries.size(); i++) {
res.add(callback
.doWith(SearchDocumentResponseBuilder.from(items[i].getResponse(), getEntityCreator(documentCallback))));
}
return res;
}
@SuppressWarnings({ "unchecked", "rawtypes" })
@Override
public List<SearchHits<?>> multiSearch(List<? extends Query> queries, List<Class<?>> classes) {
Assert.notNull(queries, "queries must not be null");
Assert.notNull(classes, "classes must not be null");
Assert.isTrue(queries.size() == classes.size(), "queries and classes must have the same size");
MultiSearchRequest request = new MultiSearchRequest();
Iterator<Class<?>> it = classes.iterator();
for (Query query : queries) {
Class<?> clazz = it.next();
request
.add(requestFactory.searchRequest(query, routingResolver.getRouting(), clazz, getIndexCoordinatesFor(clazz)));
}
MultiSearchResponse.Item[] items = getMultiSearchResult(request);
List<SearchHits<?>> res = new ArrayList<>(queries.size());
Iterator<Class<?>> it1 = classes.iterator();
for (int i = 0; i < queries.size(); i++) {
Class entityClass = it1.next();
IndexCoordinates index = getIndexCoordinatesFor(entityClass);
ReadDocumentCallback<?> documentCallback = new ReadDocumentCallback<>(elasticsearchConverter, entityClass, index);
SearchDocumentResponseCallback<SearchHits<?>> callback = new ReadSearchDocumentResponseCallback<>(entityClass,
index);
SearchResponse response = items[i].getResponse();
res.add(callback.doWith(SearchDocumentResponseBuilder.from(response, getEntityCreator(documentCallback))));
}
return res;
}
@SuppressWarnings({ "unchecked", "rawtypes" })
@Override
public List<SearchHits<?>> multiSearch(List<? extends Query> queries, List<Class<?>> classes,
IndexCoordinates index) {
Assert.notNull(queries, "queries must not be null");
Assert.notNull(classes, "classes must not be null");
Assert.notNull(index, "index must not be null");
Assert.isTrue(queries.size() == classes.size(), "queries and classes must have the same size");
MultiSearchRequest request = new MultiSearchRequest();
Iterator<Class<?>> it = classes.iterator();
for (Query query : queries) {
request.add(requestFactory.searchRequest(query, routingResolver.getRouting(), it.next(), index));
}
MultiSearchResponse.Item[] items = getMultiSearchResult(request);
List<SearchHits<?>> res = new ArrayList<>(queries.size());
Iterator<Class<?>> it1 = classes.iterator();
for (int i = 0; i < queries.size(); i++) {
Class entityClass = it1.next();
ReadDocumentCallback<?> documentCallback = new ReadDocumentCallback<>(elasticsearchConverter, entityClass, index);
SearchDocumentResponseCallback<SearchHits<?>> callback = new ReadSearchDocumentResponseCallback<>(entityClass,
index);
SearchResponse response = items[i].getResponse();
res.add(callback.doWith(SearchDocumentResponseBuilder.from(response, getEntityCreator(documentCallback))));
}
return res;
}
@Override
public List<SearchHits<?>> multiSearch(List<? extends Query> queries, List<Class<?>> classes,
List<IndexCoordinates> indexes) {
Assert.notNull(queries, "queries must not be null");
Assert.notNull(classes, "classes must not be null");
Assert.notNull(indexes, "indexes must not be null");
Assert.isTrue(queries.size() == classes.size() && queries.size() == indexes.size(),
"queries, classes and indexes must have the same size");
MultiSearchRequest request = new MultiSearchRequest();
Iterator<Class<?>> it = classes.iterator();
Iterator<IndexCoordinates> indexesIt = indexes.iterator();
for (Query query : queries) {
request.add(requestFactory.searchRequest(query, routingResolver.getRouting(), it.next(), indexesIt.next()));
}
MultiSearchResponse.Item[] items = getMultiSearchResult(request);
List<SearchHits<?>> res = new ArrayList<>(queries.size());
Iterator<Class<?>> it1 = classes.iterator();
Iterator<IndexCoordinates> indexesIt1 = indexes.iterator();
for (int i = 0; i < queries.size(); i++) {
Class entityClass = it1.next();
IndexCoordinates index = indexesIt1.next();
ReadDocumentCallback<?> documentCallback = new ReadDocumentCallback<>(elasticsearchConverter, entityClass, index);
SearchDocumentResponseCallback<SearchHits<?>> callback = new ReadSearchDocumentResponseCallback<>(entityClass,
index);
SearchResponse response = items[i].getResponse();
res.add(callback.doWith(SearchDocumentResponseBuilder.from(response, getEntityCreator(documentCallback))));
}
return res;
}
protected MultiSearchResponse.Item[] getMultiSearchResult(MultiSearchRequest request) {
MultiSearchResponse response = execute(client -> client.msearch(request, RequestOptions.DEFAULT));
MultiSearchResponse.Item[] items = response.getResponses();
Assert.isTrue(items.length == request.requests().size(), "Response should has same length with queries");
return items;
}
// endregion
// region ClientCallback
/**
* Callback interface to be used with {@link #execute(ClientCallback)} for operating directly on
* {@link RestHighLevelClient}.
*
* @since 4.0
*/
@FunctionalInterface
public interface ClientCallback<T> {
T doWithClient(RestHighLevelClient client) throws IOException;
}
/**
* Execute a callback with the {@link RestHighLevelClient}
*
* @param callback the callback to execute, must not be {@literal null}
* @param <T> the type returned from the callback
* @return the callback result
* @since 4.0
*/
public <T> T execute(ClientCallback<T> callback) {
Assert.notNull(callback, "callback must not be null");
try {
return callback.doWithClient(client);
} catch (IOException | RuntimeException e) {
throw translateException(e);
}
}
/**
* translates an Exception if possible. Exceptions that are no {@link RuntimeException}s are wrapped in a
* RuntimeException
*
* @param exception the Exception to map
* @return the potentially translated RuntimeException.
* @since 4.0
*/
private RuntimeException translateException(Exception exception) {
RuntimeException runtimeException = exception instanceof RuntimeException ? (RuntimeException) exception
: new RuntimeException(exception.getMessage(), exception);
RuntimeException potentiallyTranslatedException = exceptionTranslator
.translateExceptionIfPossible(runtimeException);
return potentiallyTranslatedException != null ? potentiallyTranslatedException : runtimeException;
}
// endregion
// region helper methods
@Override
public String getClusterVersion() {
try {
return execute(client -> client.info(RequestOptions.DEFAULT)).getVersion().getNumber();
} catch (Exception ignored) {}
return null;
}
@Override
public Query matchAllQuery() {
return new NativeSearchQueryBuilder().withQuery(QueryBuilders.matchAllQuery()).build();
}
@Override
public Query idsQuery(List<String> ids) {
Assert.notNull(ids, "ids must not be null");
return new NativeSearchQueryBuilder().withQuery(QueryBuilders.idsQuery().addIds(ids.toArray(new String[] {})))
.build();
}
@Override
public String getVendor() {
return "Elasticsearch";
}
@Override
public String getRuntimeLibraryVersion() {
return Version.CURRENT.toString();
}
@Deprecated
public SearchResponse suggest(SuggestBuilder suggestion, Class<?> clazz) {
return suggest(suggestion, getIndexCoordinatesFor(clazz));
}
// endregion
}

View File

@ -1,180 +0,0 @@
/*
* Copyright 2020-2023 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.erhlc;
import java.util.Arrays;
import java.util.stream.Collectors;
import org.elasticsearch.search.fetch.subphase.highlight.AbstractHighlighterBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentEntity;
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentProperty;
import org.springframework.data.elasticsearch.core.query.highlight.Highlight;
import org.springframework.data.elasticsearch.core.query.highlight.HighlightCommonParameters;
import org.springframework.data.elasticsearch.core.query.highlight.HighlightField;
import org.springframework.data.elasticsearch.core.query.highlight.HighlightFieldParameters;
import org.springframework.data.elasticsearch.core.query.highlight.HighlightParameters;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.lang.Nullable;
import org.springframework.util.StringUtils;
/**
* Converts the {@link Highlight} annotation from a method to an Elasticsearch 7 {@link HighlightBuilder}.
*
* @author Peter-Josef Meisch
* @deprecated since 5.0
*/
@Deprecated
public class HighlightQueryBuilder {
private final MappingContext<? extends ElasticsearchPersistentEntity<?>, ElasticsearchPersistentProperty> mappingContext;
public HighlightQueryBuilder(
MappingContext<? extends ElasticsearchPersistentEntity<?>, ElasticsearchPersistentProperty> mappingContext) {
this.mappingContext = mappingContext;
}
/**
* creates an Elasticsearch HighlightBuilder from an annotation.
*
* @param highlight, must not be {@literal null}
* @param type the entity type, used to map field names. If null, field names are not mapped.
* @return the builder for the highlight
*/
public HighlightBuilder getHighlightBuilder(Highlight highlight, @Nullable Class<?> type) {
HighlightBuilder highlightBuilder = new HighlightBuilder();
addParameters(highlight.getParameters(), highlightBuilder, type);
for (HighlightField highlightField : highlight.getFields()) {
String mappedName = mapFieldName(highlightField.getName(), type);
HighlightBuilder.Field field = new HighlightBuilder.Field(mappedName);
addParameters(highlightField.getParameters(), field, type);
highlightBuilder.field(field);
}
return highlightBuilder;
}
private <P extends HighlightCommonParameters> void addParameters(P parameters, AbstractHighlighterBuilder<?> builder,
@Nullable Class<?> type) {
if (StringUtils.hasLength(parameters.getBoundaryChars())) {
builder.boundaryChars(parameters.getBoundaryChars().toCharArray());
}
if (parameters.getBoundaryMaxScan() > -1) {
builder.boundaryMaxScan(parameters.getBoundaryMaxScan());
}
if (StringUtils.hasLength(parameters.getBoundaryScanner())) {
builder.boundaryScannerType(parameters.getBoundaryScanner());
}
if (StringUtils.hasLength(parameters.getBoundaryScannerLocale())) {
builder.boundaryScannerLocale(parameters.getBoundaryScannerLocale());
}
if (parameters.getForceSource()) { // default is false
builder.forceSource(true);
}
if (StringUtils.hasLength(parameters.getFragmenter())) {
builder.fragmenter(parameters.getFragmenter());
}
if (parameters.getFragmentSize() > -1) {
builder.fragmentSize(parameters.getFragmentSize());
}
if (parameters.getNoMatchSize() > -1) {
builder.noMatchSize(parameters.getNoMatchSize());
}
if (parameters.getNumberOfFragments() > -1) {
builder.numOfFragments(parameters.getNumberOfFragments());
}
if (StringUtils.hasLength(parameters.getOrder())) {
builder.order(parameters.getOrder());
}
if (parameters.getPhraseLimit() > -1) {
builder.phraseLimit(parameters.getPhraseLimit());
}
if (parameters.getPreTags().length > 0) {
builder.preTags(parameters.getPreTags());
}
if (parameters.getPostTags().length > 0) {
builder.postTags(parameters.getPostTags());
}
if (!parameters.getRequireFieldMatch()) { // default is true
builder.requireFieldMatch(false);
}
if (StringUtils.hasLength(parameters.getType())) {
builder.highlighterType(parameters.getType());
}
if (builder instanceof HighlightBuilder highlightBuilder
&& parameters instanceof HighlightParameters highlightParameters) {
if (StringUtils.hasLength(highlightParameters.getEncoder())) {
highlightBuilder.encoder(highlightParameters.getEncoder());
}
if (StringUtils.hasLength(highlightParameters.getTagsSchema())) {
highlightBuilder.tagsSchema(highlightParameters.getTagsSchema());
}
}
if (builder instanceof HighlightBuilder.Field field
&& parameters instanceof HighlightFieldParameters fieldParameters) {
if ((fieldParameters).getFragmentOffset() > -1) {
field.fragmentOffset(fieldParameters.getFragmentOffset());
}
if (fieldParameters.getMatchedFields().length > 0) {
field.matchedFields(Arrays.stream(fieldParameters.getMatchedFields()) //
.map(fieldName -> mapFieldName(fieldName, type)) //
.collect(Collectors.toList()) //
.toArray(new String[] {})); //
}
}
}
private String mapFieldName(String fieldName, @Nullable Class<?> type) {
if (type != null) {
ElasticsearchPersistentEntity<?> persistentEntity = mappingContext.getPersistentEntity(type);
if (persistentEntity != null) {
ElasticsearchPersistentProperty persistentProperty = persistentEntity.getPersistentProperty(fieldName);
if (persistentProperty != null) {
return persistentProperty.getFieldName();
}
}
}
return fieldName;
}
}

View File

@ -1,179 +0,0 @@
/*
* Copyright 2018-2023 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.erhlc;
import reactor.core.publisher.Mono;
import java.net.InetSocketAddress;
import java.util.Collections;
import java.util.Set;
import java.util.function.Supplier;
import org.springframework.data.elasticsearch.client.ElasticsearchHost;
import org.springframework.data.elasticsearch.client.NoReachableHostException;
import org.springframework.data.elasticsearch.support.HttpHeaders;
import org.springframework.util.Assert;
import org.springframework.web.reactive.function.client.WebClient;
/**
* Infrastructure helper class aware of hosts within the cluster and the health of those allowing easy selection of
* active ones.
*
* @author Christoph Strobl
* @author Mark Paluch
* @author Peter-Josef Meisch
* @since 3.2
* @deprecated since 5.0
*/
@Deprecated
public interface HostProvider<T extends HostProvider<T>> {
/**
* Create a new {@link HostProvider} best suited for the given {@link WebClientProvider} and number of hosts.
*
* @param clientProvider must not be {@literal null} .
* @param headersSupplier to supply custom headers, must not be {@literal null}
* @param endpoints must not be {@literal null} nor empty.
* @return new instance of {@link HostProvider}.
*/
static HostProvider<?> provider(WebClientProvider clientProvider, Supplier<HttpHeaders> headersSupplier,
InetSocketAddress... endpoints) {
Assert.notNull(clientProvider, "WebClientProvider must not be null");
Assert.notEmpty(endpoints, "Please provide at least one endpoint to connect to.");
if (endpoints.length == 1) {
return new SingleNodeHostProvider(clientProvider, endpoints[0]);
} else {
return new MultiNodeHostProvider(clientProvider, endpoints);
}
}
/**
* Lookup an active host in {@link Verification#LAZY lazy} mode utilizing cached {@link ElasticsearchHost}.
*
* @return the {@link Mono} emitting the active host or {@link Mono#error(Throwable) an error} if none found.
*/
default Mono<InetSocketAddress> lookupActiveHost() {
return lookupActiveHost(Verification.LAZY);
}
/**
* Lookup an active host in using the given {@link Verification}.
*
* @param verification
* @return the {@link Mono} emitting the active host or {@link Mono#error(Throwable) an error}
* ({@link NoReachableHostException}) if none found.
*/
Mono<InetSocketAddress> lookupActiveHost(Verification verification);
/**
* Get the {@link WebClient} connecting to an active host utilizing cached {@link ElasticsearchHost}.
*
* @return the {@link Mono} emitting the client for an active host or {@link Mono#error(Throwable) an error} if none
* found.
*/
default Mono<WebClient> getActive() {
return getActive(Verification.LAZY);
}
/**
* Get the {@link WebClient} connecting to an active host.
*
* @param verification must not be {@literal null}.
* @return the {@link Mono} emitting the client for an active host or {@link Mono#error(Throwable) an error} if none
* found.
*/
default Mono<WebClient> getActive(Verification verification) {
return lookupActiveHost(verification).map(this::createWebClient);
}
/**
* Get the {@link WebClient} connecting to an active host utilizing cached {@link ElasticsearchHost}.
*
* @return the {@link Mono} emitting the client for an active host or {@link Mono#error(Throwable) an error} if none
* found.
* @since 4.4
*/
default Mono<WebClient> getWebClient() {
return getWebClient(Verification.LAZY);
}
/**
* Get the {@link WebClient} connecting to an active host.
*
* @param verification must not be {@literal null}.
* @return the {@link Mono} emitting the client for an active host or {@link Mono#error(Throwable) an error} if none
* found.
* @since 4.4
*/
default Mono<WebClient> getWebClient(Verification verification) {
return lookupActiveHost(verification).map(this::createWebClient);
}
/**
* Creates a {@link WebClient} for {@link InetSocketAddress endpoint}.
*
* @param endpoint must not be {@literal null}.
* @return a {@link WebClient} using the the given endpoint as {@literal transport url}.
*/
WebClient createWebClient(InetSocketAddress endpoint);
/**
* Obtain information about known cluster nodes.
*
* @return the {@link Mono} emitting {@link ClusterInformation} when available.
*/
Mono<ClusterInformation> clusterInfo();
/**
* {@link Verification} allows to influence the lookup strategy for active hosts.
*
* @author Christoph Strobl
* @since 3.2
*/
enum Verification {
/**
* Actively check for cluster node health.
*/
ACTIVE,
/**
* Use cached data for cluster node health.
*/
LAZY
}
/**
* Value object accumulating information about an Elasticsearch cluster.
*
* @author Christoph Strobl
* @since 3.2
*/
class ClusterInformation {
private final Set<ElasticsearchHost> nodes;
public ClusterInformation(Set<ElasticsearchHost> nodes) {
this.nodes = nodes;
}
public Set<ElasticsearchHost> getNodes() {
return Collections.unmodifiableSet(nodes);
}
}
}

View File

@ -1,213 +0,0 @@
/*
* Copyright 2018-2023 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.erhlc;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.util.function.Tuple2;
import java.net.InetSocketAddress;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.data.elasticsearch.client.ElasticsearchHost;
import org.springframework.data.elasticsearch.client.ElasticsearchHost.State;
import org.springframework.data.elasticsearch.client.NoReachableHostException;
import org.springframework.lang.Nullable;
import org.springframework.web.reactive.function.client.ClientResponse;
import org.springframework.web.reactive.function.client.WebClient;
/**
* {@link HostProvider} for a cluster of nodes.
*
* @author Christoph Strobl
* @author Mark Paluch
* @author Peter-Josef Meisch
* @since 3.2
* @deprecated since 5.0
*/
@Deprecated
class MultiNodeHostProvider implements HostProvider<MultiNodeHostProvider> {
private final static Log LOGGER = LogFactory.getLog(MultiNodeHostProvider.class);
private final WebClientProvider clientProvider;
private final Map<InetSocketAddress, ElasticsearchHost> hosts;
MultiNodeHostProvider(WebClientProvider clientProvider, InetSocketAddress... endpoints) {
this.clientProvider = clientProvider;
this.hosts = new ConcurrentHashMap<>();
for (InetSocketAddress endpoint : endpoints) {
this.hosts.put(endpoint, new ElasticsearchHost(endpoint, State.UNKNOWN));
}
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("initialized with " + hosts);
}
}
/*
* (non-Javadoc)
* @see org.springframework.data.elasticsearch.client.erhlc.HostProvider#clusterInfo()
*/
@Override
public Mono<ClusterInformation> clusterInfo() {
return checkNodes(null).map(this::updateNodeState).buffer(hosts.size())
.then(Mono.just(new ClusterInformation(new LinkedHashSet<>(this.hosts.values()))));
}
/*
* (non-Javadoc)
* @see org.springframework.data.elasticsearch.client.erhlc.HostProvider#createWebClient(java.net.InetSocketAddress)
*/
@Override
public WebClient createWebClient(InetSocketAddress endpoint) {
return this.clientProvider.get(endpoint);
}
/*
* (non-Javadoc)
* @see org.springframework.data.elasticsearch.client.erhlc.HostProvider#lookupActiveHost(org.springframework.data.elasticsearch.client.erhlc.HostProvider.Verification)
*/
@Override
public Mono<InetSocketAddress> lookupActiveHost(Verification verification) {
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("lookupActiveHost " + verification + " from " + hosts());
}
if (Verification.LAZY.equals(verification)) {
for (ElasticsearchHost entry : hosts()) {
if (entry.isOnline()) {
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("lookupActiveHost returning " + entry);
}
return Mono.just(entry.getEndpoint());
}
}
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("no online host found with LAZY");
}
}
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("searching for active host");
}
return findActiveHostInKnownActives() //
.switchIfEmpty(findActiveHostInUnresolved()) //
.switchIfEmpty(findActiveHostInDead()) //
.switchIfEmpty(Mono.error(() -> new NoReachableHostException(new LinkedHashSet<>(getCachedHostState()))));
}
Collection<ElasticsearchHost> getCachedHostState() {
return hosts.values();
}
private Mono<InetSocketAddress> findActiveHostInKnownActives() {
return findActiveForState(State.ONLINE);
}
private Mono<InetSocketAddress> findActiveHostInUnresolved() {
return findActiveForState(State.UNKNOWN);
}
private Mono<InetSocketAddress> findActiveHostInDead() {
return findActiveForState(State.OFFLINE);
}
private Mono<InetSocketAddress> findActiveForState(State state) {
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("findActiveForState state " + state + ", current hosts: " + hosts);
}
return checkNodes(state) //
.map(this::updateNodeState) //
.filter(ElasticsearchHost::isOnline) //
.map(elasticsearchHost -> {
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("findActiveForState returning host " + elasticsearchHost);
}
return elasticsearchHost;
}).map(ElasticsearchHost::getEndpoint) //
.takeLast(1) //
.next();
}
private ElasticsearchHost updateNodeState(Tuple2<InetSocketAddress, State> tuple2) {
State state = tuple2.getT2();
ElasticsearchHost elasticsearchHost = new ElasticsearchHost(tuple2.getT1(), state);
hosts.put(tuple2.getT1(), elasticsearchHost);
return elasticsearchHost;
}
private Flux<Tuple2<InetSocketAddress, State>> checkNodes(@Nullable State state) {
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("checkNodes() with state " + state);
}
return Flux.fromIterable(hosts()) //
.filter(entry -> state == null || entry.getState().equals(state)) //
.map(ElasticsearchHost::getEndpoint) //
.concatMap(host -> {
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("checking host " + host);
}
Mono<ClientResponse> clientResponseMono = createWebClient(host) //
.head().uri("/") //
.exchangeToMono(Mono::just) //
.timeout(Duration.ofSeconds(1)) //
.doOnError(throwable -> {
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("error checking host " + host + ", " + throwable.getMessage());
}
hosts.put(host, new ElasticsearchHost(host, State.OFFLINE));
clientProvider.getErrorListener().accept(throwable);
});
return Mono.just(host) //
.zipWith(clientResponseMono.flatMap(it -> it.releaseBody() //
.thenReturn(it.statusCode().isError() ? State.OFFLINE : State.ONLINE)));
}) //
.map(tuple -> {
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("check result " + tuple);
}
return tuple;
}).onErrorContinue((throwable, o) -> clientProvider.getErrorListener().accept(throwable));
}
private List<ElasticsearchHost> hosts() {
List<ElasticsearchHost> hosts = new ArrayList<>(this.hosts.values());
Collections.shuffle(hosts);
return hosts;
}
}

View File

@ -1,176 +0,0 @@
/*
* Copyright 2020-2023 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.erhlc;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.elasticsearch.client.analytics.InferencePipelineAggregationBuilder;
import org.elasticsearch.client.analytics.ParsedInference;
import org.elasticsearch.client.analytics.ParsedStringStats;
import org.elasticsearch.client.analytics.ParsedTopMetrics;
import org.elasticsearch.client.analytics.StringStatsAggregationBuilder;
import org.elasticsearch.client.analytics.TopMetricsAggregationBuilder;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.bucket.adjacency.AdjacencyMatrixAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.adjacency.ParsedAdjacencyMatrix;
import org.elasticsearch.search.aggregations.bucket.composite.CompositeAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.composite.ParsedComposite;
import org.elasticsearch.search.aggregations.bucket.filter.FilterAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.filter.FiltersAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.filter.ParsedFilter;
import org.elasticsearch.search.aggregations.bucket.filter.ParsedFilters;
import org.elasticsearch.search.aggregations.bucket.geogrid.GeoHashGridAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.geogrid.GeoTileGridAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.geogrid.ParsedGeoHashGrid;
import org.elasticsearch.search.aggregations.bucket.geogrid.ParsedGeoTileGrid;
import org.elasticsearch.search.aggregations.bucket.global.GlobalAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.global.ParsedGlobal;
import org.elasticsearch.search.aggregations.bucket.histogram.AutoDateHistogramAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogramAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.histogram.HistogramAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.histogram.ParsedAutoDateHistogram;
import org.elasticsearch.search.aggregations.bucket.histogram.ParsedDateHistogram;
import org.elasticsearch.search.aggregations.bucket.histogram.ParsedHistogram;
import org.elasticsearch.search.aggregations.bucket.histogram.ParsedVariableWidthHistogram;
import org.elasticsearch.search.aggregations.bucket.histogram.VariableWidthHistogramAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.missing.MissingAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.missing.ParsedMissing;
import org.elasticsearch.search.aggregations.bucket.nested.NestedAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested;
import org.elasticsearch.search.aggregations.bucket.nested.ParsedReverseNested;
import org.elasticsearch.search.aggregations.bucket.nested.ReverseNestedAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.range.DateRangeAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.range.GeoDistanceAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.range.IpRangeAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.range.ParsedBinaryRange;
import org.elasticsearch.search.aggregations.bucket.range.ParsedDateRange;
import org.elasticsearch.search.aggregations.bucket.range.ParsedGeoDistance;
import org.elasticsearch.search.aggregations.bucket.range.ParsedRange;
import org.elasticsearch.search.aggregations.bucket.range.RangeAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.sampler.InternalSampler;
import org.elasticsearch.search.aggregations.bucket.sampler.ParsedSampler;
import org.elasticsearch.search.aggregations.bucket.terms.*;
import org.elasticsearch.search.aggregations.metrics.*;
import org.elasticsearch.search.aggregations.pipeline.*;
import org.elasticsearch.search.suggest.Suggest;
import org.elasticsearch.search.suggest.completion.CompletionSuggestion;
import org.elasticsearch.search.suggest.completion.CompletionSuggestionBuilder;
import org.elasticsearch.search.suggest.phrase.PhraseSuggestion;
import org.elasticsearch.search.suggest.phrase.PhraseSuggestionBuilder;
import org.elasticsearch.search.suggest.term.TermSuggestion;
import org.elasticsearch.search.suggest.term.TermSuggestionBuilder;
import org.elasticsearch.xcontent.ContextParser;
import org.elasticsearch.xcontent.NamedXContentRegistry;
import org.elasticsearch.xcontent.ParseField;
/**
* <p>
* Original implementation source {@link org.elasticsearch.client.RestHighLevelClient#getDefaultNamedXContents()} by
* {@literal Elasticsearch} (<a href="https://www.elastic.co">https://www.elastic.co</a>) licensed under the Apache
* License, Version 2.0. The latest version used from Elasticsearch is 7.10.2.
* </p>
* Modified for usage with {@link ReactiveElasticsearchClient}.
* <p>
* Only intended for internal use.
*
* @author Russell Parry
* @since 4.0
* @deprecated since 5.0
*/
@Deprecated
public class NamedXContents {
private NamedXContents() {
// contains only utility methods
}
public static List<NamedXContentRegistry.Entry> getDefaultNamedXContents() {
Map<String, ContextParser<Object, ? extends Aggregation>> map = new HashMap<>();
map.put(CardinalityAggregationBuilder.NAME, (p, c) -> ParsedCardinality.fromXContent(p, (String) c));
map.put(InternalHDRPercentiles.NAME, (p, c) -> ParsedHDRPercentiles.fromXContent(p, (String) c));
map.put(InternalHDRPercentileRanks.NAME, (p, c) -> ParsedHDRPercentileRanks.fromXContent(p, (String) c));
map.put(InternalTDigestPercentiles.NAME, (p, c) -> ParsedTDigestPercentiles.fromXContent(p, (String) c));
map.put(InternalTDigestPercentileRanks.NAME, (p, c) -> ParsedTDigestPercentileRanks.fromXContent(p, (String) c));
map.put(PercentilesBucketPipelineAggregationBuilder.NAME,
(p, c) -> ParsedPercentilesBucket.fromXContent(p, (String) c));
map.put(MedianAbsoluteDeviationAggregationBuilder.NAME,
(p, c) -> ParsedMedianAbsoluteDeviation.fromXContent(p, (String) c));
map.put(MinAggregationBuilder.NAME, (p, c) -> ParsedMin.fromXContent(p, (String) c));
map.put(MaxAggregationBuilder.NAME, (p, c) -> ParsedMax.fromXContent(p, (String) c));
map.put(SumAggregationBuilder.NAME, (p, c) -> ParsedSum.fromXContent(p, (String) c));
map.put(AvgAggregationBuilder.NAME, (p, c) -> ParsedAvg.fromXContent(p, (String) c));
map.put(WeightedAvgAggregationBuilder.NAME, (p, c) -> ParsedWeightedAvg.fromXContent(p, (String) c));
map.put(ValueCountAggregationBuilder.NAME, (p, c) -> ParsedValueCount.fromXContent(p, (String) c));
map.put(InternalSimpleValue.NAME, (p, c) -> ParsedSimpleValue.fromXContent(p, (String) c));
map.put(DerivativePipelineAggregationBuilder.NAME, (p, c) -> ParsedDerivative.fromXContent(p, (String) c));
map.put(InternalBucketMetricValue.NAME, (p, c) -> ParsedBucketMetricValue.fromXContent(p, (String) c));
map.put(StatsAggregationBuilder.NAME, (p, c) -> ParsedStats.fromXContent(p, (String) c));
map.put(StatsBucketPipelineAggregationBuilder.NAME, (p, c) -> ParsedStatsBucket.fromXContent(p, (String) c));
map.put(ExtendedStatsAggregationBuilder.NAME, (p, c) -> ParsedExtendedStats.fromXContent(p, (String) c));
map.put(ExtendedStatsBucketPipelineAggregationBuilder.NAME,
(p, c) -> ParsedExtendedStatsBucket.fromXContent(p, (String) c));
map.put(GeoBoundsAggregationBuilder.NAME, (p, c) -> ParsedGeoBounds.fromXContent(p, (String) c));
map.put(GeoCentroidAggregationBuilder.NAME, (p, c) -> ParsedGeoCentroid.fromXContent(p, (String) c));
map.put(HistogramAggregationBuilder.NAME, (p, c) -> ParsedHistogram.fromXContent(p, (String) c));
map.put(DateHistogramAggregationBuilder.NAME, (p, c) -> ParsedDateHistogram.fromXContent(p, (String) c));
map.put(AutoDateHistogramAggregationBuilder.NAME, (p, c) -> ParsedAutoDateHistogram.fromXContent(p, (String) c));
map.put(VariableWidthHistogramAggregationBuilder.NAME,
(p, c) -> ParsedVariableWidthHistogram.fromXContent(p, (String) c));
map.put(StringTerms.NAME, (p, c) -> ParsedStringTerms.fromXContent(p, (String) c));
map.put(LongTerms.NAME, (p, c) -> ParsedLongTerms.fromXContent(p, (String) c));
map.put(DoubleTerms.NAME, (p, c) -> ParsedDoubleTerms.fromXContent(p, (String) c));
map.put(LongRareTerms.NAME, (p, c) -> ParsedLongRareTerms.fromXContent(p, (String) c));
map.put(StringRareTerms.NAME, (p, c) -> ParsedStringRareTerms.fromXContent(p, (String) c));
map.put(MissingAggregationBuilder.NAME, (p, c) -> ParsedMissing.fromXContent(p, (String) c));
map.put(NestedAggregationBuilder.NAME, (p, c) -> ParsedNested.fromXContent(p, (String) c));
map.put(ReverseNestedAggregationBuilder.NAME, (p, c) -> ParsedReverseNested.fromXContent(p, (String) c));
map.put(GlobalAggregationBuilder.NAME, (p, c) -> ParsedGlobal.fromXContent(p, (String) c));
map.put(FilterAggregationBuilder.NAME, (p, c) -> ParsedFilter.fromXContent(p, (String) c));
map.put(InternalSampler.PARSER_NAME, (p, c) -> ParsedSampler.fromXContent(p, (String) c));
map.put(GeoHashGridAggregationBuilder.NAME, (p, c) -> ParsedGeoHashGrid.fromXContent(p, (String) c));
map.put(GeoTileGridAggregationBuilder.NAME, (p, c) -> ParsedGeoTileGrid.fromXContent(p, (String) c));
map.put(RangeAggregationBuilder.NAME, (p, c) -> ParsedRange.fromXContent(p, (String) c));
map.put(DateRangeAggregationBuilder.NAME, (p, c) -> ParsedDateRange.fromXContent(p, (String) c));
map.put(GeoDistanceAggregationBuilder.NAME, (p, c) -> ParsedGeoDistance.fromXContent(p, (String) c));
map.put(FiltersAggregationBuilder.NAME, (p, c) -> ParsedFilters.fromXContent(p, (String) c));
map.put(AdjacencyMatrixAggregationBuilder.NAME, (p, c) -> ParsedAdjacencyMatrix.fromXContent(p, (String) c));
map.put(SignificantLongTerms.NAME, (p, c) -> ParsedSignificantLongTerms.fromXContent(p, (String) c));
map.put(SignificantStringTerms.NAME, (p, c) -> ParsedSignificantStringTerms.fromXContent(p, (String) c));
map.put(ScriptedMetricAggregationBuilder.NAME, (p, c) -> ParsedScriptedMetric.fromXContent(p, (String) c));
map.put(IpRangeAggregationBuilder.NAME, (p, c) -> ParsedBinaryRange.fromXContent(p, (String) c));
map.put(TopHitsAggregationBuilder.NAME, (p, c) -> ParsedTopHits.fromXContent(p, (String) c));
map.put(CompositeAggregationBuilder.NAME, (p, c) -> ParsedComposite.fromXContent(p, (String) c));
map.put(StringStatsAggregationBuilder.NAME, (p, c) -> ParsedStringStats.PARSER.parse(p, (String) c));
map.put(TopMetricsAggregationBuilder.NAME, (p, c) -> ParsedTopMetrics.PARSER.parse(p, (String) c));
map.put(InferencePipelineAggregationBuilder.NAME, (p, c) -> ParsedInference.fromXContent(p, (String) (c)));
List<NamedXContentRegistry.Entry> entries = map.entrySet().stream().map(
entry -> new NamedXContentRegistry.Entry(Aggregation.class, new ParseField(entry.getKey()), entry.getValue()))
.collect(Collectors.toList());
entries.add(
new NamedXContentRegistry.Entry(Suggest.Suggestion.class, new ParseField(TermSuggestionBuilder.SUGGESTION_NAME),
(parser, context) -> TermSuggestion.fromXContent(parser, (String) context)));
entries.add(new NamedXContentRegistry.Entry(Suggest.Suggestion.class,
new ParseField(PhraseSuggestionBuilder.SUGGESTION_NAME),
(parser, context) -> PhraseSuggestion.fromXContent(parser, (String) context)));
entries.add(new NamedXContentRegistry.Entry(Suggest.Suggestion.class,
new ParseField(CompletionSuggestionBuilder.SUGGESTION_NAME),
(parser, context) -> CompletionSuggestion.fromXContent(parser, (String) context)));
return entries;
}
}

View File

@ -1,231 +0,0 @@
/*
* Copyright 2013-2023 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.erhlc;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.script.mustache.SearchTemplateRequestBuilder;
import org.elasticsearch.search.SearchExtBuilder;
import org.elasticsearch.search.aggregations.AbstractAggregationBuilder;
import org.elasticsearch.search.aggregations.PipelineAggregationBuilder;
import org.elasticsearch.search.collapse.CollapseBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.SortBuilder;
import org.elasticsearch.search.suggest.SuggestBuilder;
import org.springframework.data.elasticsearch.core.query.BaseQuery;
import org.springframework.data.elasticsearch.core.query.IndexBoost;
import org.springframework.lang.Nullable;
/**
* A query created from Elasticsearch QueryBuilder instances. Note: the filter constructor parameter is used to create a
* post_filter
* {@see https://www.elastic.co/guide/en/elasticsearch/reference/7.9/filter-search-results.html#post-filter}, if a
* filter is needed that filters before aggregations are build, it must be included in the query constructor parameter.
*
* @author Rizwan Idrees
* @author Mohsin Husen
* @author Artur Konczak
* @author Jean-Baptiste Nizet
* @author Martin Choraine
* @author Peter-Josef Meisch
* @author Sijia Liu
* @deprecated since 5.0
*/
@Deprecated
public class NativeSearchQuery extends BaseQuery {
@Nullable private final QueryBuilder query;
@Nullable private QueryBuilder filter;
@Nullable private List<SortBuilder<?>> sorts;
private final List<ScriptField> scriptFields = new ArrayList<>();
@Nullable private CollapseBuilder collapseBuilder;
@Nullable private List<AbstractAggregationBuilder<?>> aggregations;
@Nullable private List<PipelineAggregationBuilder> pipelineAggregations;
@Nullable private HighlightBuilder highlightBuilder;
@Nullable private HighlightBuilder.Field[] highlightFields;
@Nullable private SearchTemplateRequestBuilder searchTemplate;
@Nullable private SuggestBuilder suggestBuilder;
@Nullable private List<SearchExtBuilder> searchExtBuilders;
public NativeSearchQuery(@Nullable QueryBuilder query) {
this.query = query;
}
public NativeSearchQuery(@Nullable QueryBuilder query, @Nullable QueryBuilder filter) {
this.query = query;
this.filter = filter;
}
public NativeSearchQuery(@Nullable QueryBuilder query, @Nullable QueryBuilder filter,
@Nullable List<SortBuilder<?>> sorts) {
this.query = query;
this.filter = filter;
this.sorts = sorts;
}
public NativeSearchQuery(@Nullable QueryBuilder query, @Nullable QueryBuilder filter,
@Nullable List<SortBuilder<?>> sorts, @Nullable HighlightBuilder.Field[] highlightFields) {
this.query = query;
this.filter = filter;
this.sorts = sorts;
this.highlightFields = highlightFields;
}
public NativeSearchQuery(@Nullable QueryBuilder query, @Nullable QueryBuilder filter,
@Nullable List<SortBuilder<?>> sorts, @Nullable HighlightBuilder highlightBuilder,
@Nullable HighlightBuilder.Field[] highlightFields) {
this.query = query;
this.filter = filter;
this.sorts = sorts;
this.highlightBuilder = highlightBuilder;
this.highlightFields = highlightFields;
}
public NativeSearchQuery(NativeSearchQueryBuilder builder, @Nullable QueryBuilder query,
@Nullable QueryBuilder filter, @Nullable List<SortBuilder<?>> sorts, @Nullable HighlightBuilder highlightBuilder,
@Nullable HighlightBuilder.Field[] highlightFields) {
super(builder);
this.query = query;
this.filter = filter;
this.sorts = sorts;
this.highlightBuilder = highlightBuilder;
this.highlightFields = highlightFields;
}
@Nullable
public QueryBuilder getQuery() {
return query;
}
@Nullable
public QueryBuilder getFilter() {
return filter;
}
@Nullable
public List<SortBuilder<?>> getElasticsearchSorts() {
return sorts;
}
@Nullable
public HighlightBuilder getHighlightBuilder() {
return highlightBuilder;
}
@Nullable
public HighlightBuilder.Field[] getHighlightFields() {
return highlightFields;
}
public List<ScriptField> getScriptFields() {
return scriptFields;
}
public void setScriptFields(List<ScriptField> scriptFields) {
this.scriptFields.addAll(scriptFields);
}
public void addScriptField(ScriptField... scriptField) {
scriptFields.addAll(Arrays.asList(scriptField));
}
@Nullable
public CollapseBuilder getCollapseBuilder() {
return collapseBuilder;
}
public void setCollapseBuilder(CollapseBuilder collapseBuilder) {
this.collapseBuilder = collapseBuilder;
}
@Nullable
public List<AbstractAggregationBuilder<?>> getAggregations() {
return aggregations;
}
@Nullable
public List<PipelineAggregationBuilder> getPipelineAggregations() {
return pipelineAggregations;
}
public void addAggregation(AbstractAggregationBuilder<?> aggregationBuilder) {
if (aggregations == null) {
aggregations = new ArrayList<>();
}
aggregations.add(aggregationBuilder);
}
public void setAggregations(List<AbstractAggregationBuilder<?>> aggregations) {
this.aggregations = aggregations;
}
public void setPipelineAggregations(List<PipelineAggregationBuilder> pipelineAggregationBuilders) {
this.pipelineAggregations = pipelineAggregationBuilders;
}
public void setIndicesBoost(List<IndexBoost> indicesBoost) {
this.indicesBoost = indicesBoost;
}
@Nullable
public SearchTemplateRequestBuilder getSearchTemplate() {
return searchTemplate;
}
public void setSearchTemplate(@Nullable SearchTemplateRequestBuilder searchTemplate) {
this.searchTemplate = searchTemplate;
}
/**
* @since 4.3
*/
public void setSuggestBuilder(SuggestBuilder suggestBuilder) {
this.suggestBuilder = suggestBuilder;
}
/**
* @since 4.3
*/
@Nullable
public SuggestBuilder getSuggestBuilder() {
return suggestBuilder;
}
public void setSearchExtBuilders(List<SearchExtBuilder> searchExtBuilders) {
this.searchExtBuilders = searchExtBuilders;
}
public void addSearchExtBuilder(SearchExtBuilder searchExtBuilder) {
if (searchExtBuilders == null) {
searchExtBuilders = new ArrayList<>();
}
searchExtBuilders.add(searchExtBuilder);
}
@Nullable
public List<SearchExtBuilder> getSearchExtBuilders() {
return searchExtBuilders;
}
}

View File

@ -1,328 +0,0 @@
/*
* Copyright 2013-2023 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.erhlc;
import static org.springframework.util.CollectionUtils.*;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import org.elasticsearch.action.search.SearchType;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.script.mustache.SearchTemplateRequestBuilder;
import org.elasticsearch.search.aggregations.AbstractAggregationBuilder;
import org.elasticsearch.search.aggregations.PipelineAggregationBuilder;
import org.elasticsearch.search.collapse.CollapseBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.SortBuilder;
import org.elasticsearch.search.suggest.SuggestBuilder;
import org.springframework.data.elasticsearch.core.query.BaseQueryBuilder;
import org.springframework.data.elasticsearch.core.query.IndicesOptions;
import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.data.elasticsearch.core.query.RescorerQuery;
import org.springframework.lang.Nullable;
/**
* NativeSearchQuery
*
* @author Rizwan Idrees
* @author Mohsin Husen
* @author Artur Konczak
* @author Mark Paluch
* @author Alen Turkovic
* @author Sascha Woo
* @author Jean-Baptiste Nizet
* @author Martin Choraine
* @author Farid Azaza
* @author Peter-Josef Meisch
* @author Peer Mueller
* @author vdisk
* @author owen.qq
* @deprecated since 5.0
*/
@Deprecated
public class NativeSearchQueryBuilder extends BaseQueryBuilder<NativeSearchQuery, NativeSearchQueryBuilder> {
@Nullable private QueryBuilder queryBuilder;
@Nullable private QueryBuilder filterBuilder;
private final List<ScriptField> scriptFields = new ArrayList<>();
private final List<SortBuilder<?>> sortBuilders = new ArrayList<>();
private final List<AbstractAggregationBuilder<?>> aggregationBuilders = new ArrayList<>();
private final List<PipelineAggregationBuilder> pipelineAggregationBuilders = new ArrayList<>();
@Nullable private HighlightBuilder highlightBuilder;
@Nullable private List<HighlightBuilder.Field> highlightFields = new ArrayList<>();
@Nullable protected List<String> storedFields;
@Nullable private CollapseBuilder collapseBuilder;
@Nullable private SearchTemplateRequestBuilder searchTemplateBuilder;
@Nullable private SearchType searchType;
@Nullable private Boolean trackTotalHits;
@Nullable private Duration timeout;
private final List<RescorerQuery> rescorerQueries = new ArrayList<>();
@Nullable private SuggestBuilder suggestBuilder;
@Nullable private List<Object> searchAfter;
public NativeSearchQueryBuilder withQuery(QueryBuilder queryBuilder) {
this.queryBuilder = queryBuilder;
return this;
}
public NativeSearchQueryBuilder withFilter(QueryBuilder filterBuilder) {
this.filterBuilder = filterBuilder;
return this;
}
/**
* @deprecated use {@link #withSorts(SortBuilder...)} instead.
*/
@Deprecated
public NativeSearchQueryBuilder withSort(SortBuilder<?> sortBuilder) {
this.sortBuilders.add(sortBuilder);
return this;
}
/**
* @since 4.3
*/
public NativeSearchQueryBuilder withSorts(Collection<SortBuilder<?>> sortBuilders) {
this.sortBuilders.addAll(sortBuilders);
return this;
}
/**
* @since 4.3
*/
public NativeSearchQueryBuilder withSorts(SortBuilder<?>... sortBuilders) {
Collections.addAll(this.sortBuilders, sortBuilders);
return this;
}
public NativeSearchQueryBuilder withScriptField(ScriptField scriptField) {
this.scriptFields.add(scriptField);
return this;
}
public NativeSearchQueryBuilder withCollapseField(String collapseField) {
this.collapseBuilder = new CollapseBuilder(collapseField);
return this;
}
/**
* @since 4.3
*/
public NativeSearchQueryBuilder withCollapseBuilder(@Nullable CollapseBuilder collapseBuilder) {
this.collapseBuilder = collapseBuilder;
return this;
}
/**
* @deprecated use {@link #withAggregations(AbstractAggregationBuilder...)} instead.
*/
@Deprecated
public NativeSearchQueryBuilder addAggregation(AbstractAggregationBuilder<?> aggregationBuilder) {
this.aggregationBuilders.add(aggregationBuilder);
return this;
}
/**
* @since 4.3
*/
public NativeSearchQueryBuilder withAggregations(Collection<AbstractAggregationBuilder<?>> aggregationBuilders) {
this.aggregationBuilders.addAll(aggregationBuilders);
return this;
}
/**
* @since 4.3
*/
public NativeSearchQueryBuilder withAggregations(AbstractAggregationBuilder<?>... aggregationBuilders) {
Collections.addAll(this.aggregationBuilders, aggregationBuilders);
return this;
}
/**
* @deprecated use {@link #withPipelineAggregations(PipelineAggregationBuilder...)} instead.
*/
@Deprecated
public NativeSearchQueryBuilder addAggregation(PipelineAggregationBuilder pipelineAggregationBuilder) {
this.pipelineAggregationBuilders.add(pipelineAggregationBuilder);
return this;
}
/**
* @since 4.3
*/
public NativeSearchQueryBuilder withPipelineAggregations(
Collection<PipelineAggregationBuilder> pipelineAggregationBuilders) {
this.pipelineAggregationBuilders.addAll(pipelineAggregationBuilders);
return this;
}
/**
* @since 4.3
*/
public NativeSearchQueryBuilder withPipelineAggregations(PipelineAggregationBuilder... pipelineAggregationBuilders) {
Collections.addAll(this.pipelineAggregationBuilders, pipelineAggregationBuilders);
return this;
}
public NativeSearchQueryBuilder withHighlightBuilder(HighlightBuilder highlightBuilder) {
this.highlightBuilder = highlightBuilder;
return this;
}
public NativeSearchQueryBuilder withHighlightFields(HighlightBuilder.Field... highlightFields) {
Collections.addAll(this.highlightFields, highlightFields);
return this;
}
/**
* @since 4.3
*/
public NativeSearchQueryBuilder withHighlightFields(Collection<HighlightBuilder.Field> highlightFields) {
this.highlightFields.addAll(highlightFields);
return this;
}
public NativeSearchQueryBuilder withSearchTemplate(SearchTemplateRequestBuilder searchTemplateBuilder) {
this.searchTemplateBuilder = searchTemplateBuilder;
return this;
}
public NativeSearchQueryBuilder withStoredFields(Collection<String> storedFields) {
if (this.storedFields == null) {
this.storedFields = new ArrayList<>(storedFields);
} else {
this.storedFields.addAll(storedFields);
}
return this;
}
public NativeSearchQueryBuilder withStoredFields(String... storedFields) {
if (this.storedFields == null) {
this.storedFields = new ArrayList<>(storedFields.length);
}
Collections.addAll(this.storedFields, storedFields);
return this;
}
public NativeSearchQueryBuilder withSearchType(SearchType searchType) {
this.searchType = searchType;
return this;
}
/**
* @since 4.2
*/
public NativeSearchQueryBuilder withTrackTotalHits(Boolean trackTotalHits) {
this.trackTotalHits = trackTotalHits;
return this;
}
public NativeSearchQueryBuilder withTimeout(Duration timeout) {
this.timeout = timeout;
return this;
}
public NativeSearchQueryBuilder withRescorerQuery(RescorerQuery rescorerQuery) {
this.rescorerQueries.add(rescorerQuery);
return this;
}
/**
* @since 4.3
*/
public NativeSearchQueryBuilder withSuggestBuilder(SuggestBuilder suggestBuilder) {
this.suggestBuilder = suggestBuilder;
return this;
}
public NativeSearchQueryBuilder withSearchAfter(List<Object> searchAfter) {
if (searchAfter != null && searchAfter.isEmpty()) {
return this;
}
this.searchAfter = searchAfter;
return this;
}
public NativeSearchQuery build() {
NativeSearchQuery nativeSearchQuery = new NativeSearchQuery( //
this, //
queryBuilder, //
filterBuilder, //
sortBuilders, //
highlightBuilder, //
highlightFields.toArray(new HighlightBuilder.Field[highlightFields.size()]));
if (storedFields != null) {
nativeSearchQuery.setStoredFields(storedFields);
}
if (searchTemplateBuilder != null) {
nativeSearchQuery.setSearchTemplate(searchTemplateBuilder);
}
if (!isEmpty(scriptFields)) {
nativeSearchQuery.setScriptFields(scriptFields);
}
if (collapseBuilder != null) {
nativeSearchQuery.setCollapseBuilder(collapseBuilder);
}
if (!isEmpty(aggregationBuilders)) {
nativeSearchQuery.setAggregations(aggregationBuilders);
}
if (!isEmpty(pipelineAggregationBuilders)) {
nativeSearchQuery.setPipelineAggregations(pipelineAggregationBuilders);
}
if (searchType != null) {
nativeSearchQuery.setSearchType(Query.SearchType.valueOf(searchType.name()));
}
if (getIndicesOptions() != null) {
nativeSearchQuery.setIndicesOptions(getIndicesOptions());
}
nativeSearchQuery.setTrackTotalHits(trackTotalHits);
if (timeout != null) {
nativeSearchQuery.setTimeout(timeout);
}
if (!isEmpty(rescorerQueries)) {
nativeSearchQuery.setRescorerQueries(rescorerQueries);
}
if (suggestBuilder != null) {
nativeSearchQuery.setSuggestBuilder(suggestBuilder);
}
if (searchAfter != null) {
nativeSearchQuery.setSearchAfter(searchAfter);
}
return nativeSearchQuery;
}
}

View File

@ -1,87 +0,0 @@
/*
* Copyright 2018-2023 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.erhlc;
import reactor.core.publisher.Mono;
import java.io.IOException;
import org.elasticsearch.action.ActionResponse;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.springframework.http.HttpStatusCode;
import org.springframework.http.client.reactive.ClientHttpResponse;
import org.springframework.web.reactive.function.BodyExtractor;
import org.springframework.web.reactive.function.client.ClientResponse;
/**
* Extension to {@link ActionResponse} that also delegates to {@link ClientResponse}.
*
* @author Christoph Strobl
* @author Peter-Josef Meisch
* @author Mark Paluch
* @author Oliver Drotbohm
* @since 3.2
* @deprecated since 5.0
*/
@Deprecated
class RawActionResponse extends ActionResponse {
private final ClientResponse delegate;
private RawActionResponse(ClientResponse delegate) {
this.delegate = delegate;
}
static RawActionResponse create(ClientResponse response) {
return new RawActionResponse(response);
}
public HttpStatusCode statusCode() {
return delegate.statusCode();
}
/*
* (non-Javadoc)
* @see org.springframework.web.reactive.function.client.ClientResponse#headers()
*/
public ClientResponse.Headers headers() {
return delegate.headers();
}
/*
* (non-Javadoc)
* @see org.springframework.web.reactive.function.client.ClientResponse#body(org.springframework.web.reactive.function.BodyExtractor)
*/
public <T> T body(BodyExtractor<T, ? super ClientHttpResponse> extractor) {
return delegate.body(extractor);
}
/*
* (non-Javadoc)
* until Elasticsearch 7.4 this empty implementation was available in the abstract base class
*/
@Override
public void writeTo(StreamOutput out) throws IOException {}
/**
* Ensure the response body is released to properly release the underlying connection.
*
* @return
*/
public Mono<Void> releaseBody() {
return delegate.releaseBody();
}
}

View File

@ -1,779 +0,0 @@
/*
* Copyright 2018-2023 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.erhlc;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.util.function.Tuple2;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.elasticsearch.Version;
import org.elasticsearch.action.DocWriteResponse;
import org.elasticsearch.action.bulk.BulkItemResponse;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.MultiGetRequest;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.support.IndicesOptions;
import org.elasticsearch.action.support.WriteRequest;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.index.get.GetResult;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.reindex.BulkByScrollResponse;
import org.elasticsearch.index.reindex.DeleteByQueryRequest;
import org.elasticsearch.index.reindex.UpdateByQueryRequest;
import org.reactivestreams.Publisher;
import org.springframework.data.elasticsearch.BulkFailureException;
import org.springframework.data.elasticsearch.NoSuchIndexException;
import org.springframework.data.elasticsearch.UncategorizedElasticsearchException;
import org.springframework.data.elasticsearch.core.*;
import org.springframework.data.elasticsearch.core.cluster.ReactiveClusterOperations;
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
import org.springframework.data.elasticsearch.core.document.SearchDocument;
import org.springframework.data.elasticsearch.core.document.SearchDocumentResponse;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.*;
import org.springframework.data.elasticsearch.core.reindex.ReindexRequest;
import org.springframework.data.elasticsearch.core.reindex.ReindexResponse;
import org.springframework.http.HttpStatus;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
* @author Christoph Strobl
* @author Mark Paluch
* @author Farid Azaza
* @author Martin Choraine
* @author Peter-Josef Meisch
* @author Mathias Teier
* @author Aleksei Arsenev
* @author Roman Puchkovskiy
* @author Russell Parry
* @author Thomas Geese
* @author Farid Faoudi
* @author Sijia Liu
* @since 3.2
* @deprecated since 5.0
*/
@Deprecated
public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearchTemplate {
private final ReactiveElasticsearchClient client;
private final ElasticsearchExceptionTranslator exceptionTranslator;
protected RequestFactory requestFactory;
private @Nullable IndicesOptions indicesOptions = IndicesOptions.strictExpandOpenAndForbidClosedIgnoreThrottled();
// region Initialization
public ReactiveElasticsearchTemplate(ReactiveElasticsearchClient client) {
this(client, null);
}
public ReactiveElasticsearchTemplate(ReactiveElasticsearchClient client, @Nullable ElasticsearchConverter converter) {
super(converter);
Assert.notNull(client, "client must not be null");
this.client = client;
this.exceptionTranslator = new ElasticsearchExceptionTranslator();
this.requestFactory = new RequestFactory(this.converter);
}
protected ReactiveElasticsearchTemplate doCopy() {
ReactiveElasticsearchTemplate copy = new ReactiveElasticsearchTemplate(client, converter);
copy.setIndicesOptions(indicesOptions);
return copy;
}
/**
* Set the default {@link IndicesOptions} for {@link SearchRequest search requests}.
*
* @param indicesOptions can be {@literal null}.
*/
public void setIndicesOptions(@Nullable IndicesOptions indicesOptions) {
this.indicesOptions = indicesOptions;
}
// endregion
// region DocumentOperations
@Override
public <T> Flux<T> saveAll(Mono<? extends Collection<? extends T>> entitiesPublisher, IndexCoordinates index) {
Assert.notNull(entitiesPublisher, "entitiesPublisher must not be null!");
return entitiesPublisher //
.flatMapMany(entities -> Flux.fromIterable(entities) //
.concatMap(entity -> maybeCallbackBeforeConvert(entity, index)) //
).collectList() //
.map(Entities::new) //
.flatMapMany(entities -> {
if (entities.isEmpty()) {
return Flux.empty();
}
return doBulkOperation(entities.indexQueries(), BulkOptions.defaultOptions(), index) //
.index() //
.flatMap(indexAndResponse -> {
T savedEntity = entities.entityAt(indexAndResponse.getT1());
BulkItemResponse bulkItemResponse = indexAndResponse.getT2();
DocWriteResponse response = bulkItemResponse.getResponse();
updateIndexedObject(savedEntity, new IndexedObjectInformation(response.getId(), response.getIndex(),
response.getSeqNo(), response.getPrimaryTerm(), response.getVersion()));
return maybeCallbackAfterSave(savedEntity, index);
});
});
}
@Override
public <T> Flux<MultiGetItem<T>> multiGet(Query query, Class<T> clazz, IndexCoordinates index) {
Assert.notNull(index, "Index must not be null");
Assert.notNull(clazz, "Class must not be null");
Assert.notNull(query, "Query must not be null");
DocumentCallback<T> callback = new ReadDocumentCallback<>(converter, clazz, index);
MultiGetRequest request = requestFactory.multiGetRequest(query, clazz, index);
return Flux.from(execute(client -> client.multiGet(request))) //
.map(DocumentAdapters::from) //
.flatMap(multiGetItem -> multiGetItem.isFailed() //
? Mono.just(MultiGetItem.of(null, multiGetItem.getFailure())) //
: callback.toEntity(multiGetItem.getItem())
.map((T item) -> MultiGetItem.of(item, multiGetItem.getFailure())) //
);
}
/**
* Customization hook on the actual execution result {@link Publisher}. <br />
* You know what you're doing here? Well fair enough, go ahead on your own risk.
*
* @param request the already prepared {@link IndexRequest} ready to be executed.
* @return a {@link Mono} emitting the result of the operation.
*/
protected Mono<IndexResponse> doIndex(IndexRequest request) {
return Mono.from(execute(client -> client.index(request)));
}
@Override
public Mono<Void> bulkUpdate(List<UpdateQuery> queries, BulkOptions bulkOptions, IndexCoordinates index) {
Assert.notNull(queries, "List of UpdateQuery must not be null");
Assert.notNull(bulkOptions, "BulkOptions must not be null");
Assert.notNull(index, "Index must not be null");
return doBulkOperation(queries, bulkOptions, index).then();
}
protected Flux<BulkItemResponse> doBulkOperation(List<?> queries, BulkOptions bulkOptions, IndexCoordinates index) {
BulkRequest bulkRequest = prepareWriteRequest(requestFactory.bulkRequest(queries, bulkOptions, index));
return client.bulk(bulkRequest) //
.onErrorMap(e -> new UncategorizedElasticsearchException("Error while bulk for request: " + bulkRequest, e)) //
.flatMap(this::checkForBulkOperationFailure) //
.flatMapMany(response -> Flux.fromArray(response.getItems()));
}
protected Mono<BulkResponse> checkForBulkOperationFailure(BulkResponse bulkResponse) {
if (bulkResponse.hasFailures()) {
Map<String, String> failedDocuments = new HashMap<>();
for (BulkItemResponse item : bulkResponse.getItems()) {
if (item.isFailed()) {
failedDocuments.put(item.getId(), item.getFailureMessage());
}
}
BulkFailureException exception = new BulkFailureException(
"Bulk operation has failures. Use ElasticsearchException.getFailedDocuments() for detailed messages ["
+ failedDocuments + ']',
failedDocuments);
return Mono.error(exception);
} else {
return Mono.just(bulkResponse);
}
}
protected Mono<Boolean> doExists(String id, IndexCoordinates index) {
return Mono.defer(() -> doExists(requestFactory.getRequest(id, routingResolver.getRouting(), index)));
}
/**
* Customization hook on the actual execution result {@link Publisher}. <br />
*
* @param request the already prepared {@link GetRequest} ready to be executed.
* @return a {@link Mono} emitting the result of the operation.
*/
protected Mono<Boolean> doExists(GetRequest request) {
return Mono.from(execute(client -> client.exists(request))) //
.onErrorReturn(NoSuchIndexException.class, false);
}
protected <T> Mono<Tuple2<T, IndexResponseMetaData>> doIndex(T entity, IndexCoordinates index) {
IndexRequest request = requestFactory.indexRequest(getIndexQuery(entity), index);
request = prepareIndexRequest(entity, request);
return Mono.just(entity).zipWith(doIndex(request) //
.map(indexResponse -> new IndexResponseMetaData( //
indexResponse.getId(), //
indexResponse.getIndex(), //
indexResponse.getSeqNo(), //
indexResponse.getPrimaryTerm(), //
indexResponse.getVersion() //
)));
}
@Override
public <T> Mono<T> get(String id, Class<T> entityType, IndexCoordinates index) {
Assert.notNull(id, "Id must not be null!");
GetRequest request = requestFactory.getRequest(id, routingResolver.getRouting(), index);
Mono<GetResult> getResult = doGet(request);
DocumentCallback<T> callback = new ReadDocumentCallback<>(converter, entityType, index);
return getResult.flatMap(response -> callback.toEntity(DocumentAdapters.from(response)));
}
/**
* Customization hook on the actual execution result {@link Publisher}. <br />
*
* @param request the already prepared {@link GetRequest} ready to be executed.
* @return a {@link Mono} emitting the result of the operation.
*/
protected Mono<GetResult> doGet(GetRequest request) {
return Mono.from(execute(client -> client.get(request)));
}
protected Mono<String> doDeleteById(String id, @Nullable String routing, IndexCoordinates index) {
return Mono.defer(() -> {
DeleteRequest request = requestFactory.deleteRequest(id, routing, index);
return doDelete(prepareDeleteRequest(request));
});
}
/*
* (non-Javadoc)
* @see org.springframework.data.elasticsearch.core.ReactiveElasticsearchOperations#delete(Query, Class, IndexCoordinates)
*/
@Override
public Mono<ByQueryResponse> delete(Query query, Class<?> entityType, IndexCoordinates index) {
Assert.notNull(query, "Query must not be null!");
return doDeleteBy(query, entityType, index).map(ResponseConverter::byQueryResponseOf);
}
@Override
public Mono<UpdateResponse> update(UpdateQuery updateQuery, IndexCoordinates index) {
Assert.notNull(updateQuery, "UpdateQuery must not be null");
Assert.notNull(index, "Index must not be null");
return Mono.defer(() -> {
UpdateRequest request = requestFactory.updateRequest(updateQuery, index);
if (updateQuery.getRefreshPolicy() == null && refreshPolicy != null) {
request.setRefreshPolicy(RequestFactory.toElasticsearchRefreshPolicy(refreshPolicy));
}
if (updateQuery.getRouting() == null && routingResolver.getRouting() != null) {
request.routing(routingResolver.getRouting());
}
return Mono.from(execute(client -> client.update(request)))
.map(response -> new UpdateResponse(UpdateResponse.Result.valueOf(response.getResult().name())));
});
}
@Override
public Mono<ByQueryResponse> updateByQuery(UpdateQuery updateQuery, IndexCoordinates index) {
Assert.notNull(updateQuery, "updateQuery must not be null");
Assert.notNull(index, "Index must not be null");
return Mono.defer(() -> {
final UpdateByQueryRequest request = requestFactory.updateByQueryRequest(updateQuery, index);
if (updateQuery.getRefreshPolicy() == null && refreshPolicy != null) {
request.setRefresh(refreshPolicy == RefreshPolicy.IMMEDIATE);
}
if (updateQuery.getRouting() == null && routingResolver.getRouting() != null) {
request.setRouting(routingResolver.getRouting());
}
return Mono.from(execute(client -> client.updateBy(request)));
});
}
@Override
public Mono<ReindexResponse> reindex(ReindexRequest postReindexRequest) {
Assert.notNull(postReindexRequest, "postReindexRequest must not be null");
return Mono.defer(() -> {
org.elasticsearch.index.reindex.ReindexRequest reindexRequest = requestFactory.reindexRequest(postReindexRequest);
return Mono.from(execute(client -> client.reindex(reindexRequest))).map(ResponseConverter::reindexResponseOf);
});
}
@Override
public Mono<String> submitReindex(ReindexRequest postReindexRequest) {
Assert.notNull(postReindexRequest, "postReindexRequest must not be null");
return Mono.defer(() -> {
org.elasticsearch.index.reindex.ReindexRequest reindexRequest = requestFactory.reindexRequest(postReindexRequest);
return Mono.from(execute(client -> client.submitReindex(reindexRequest)));
});
}
protected Mono<BulkByScrollResponse> doDeleteBy(Query query, Class<?> entityType, IndexCoordinates index) {
return Mono.defer(() -> {
DeleteByQueryRequest request = requestFactory.deleteByQueryRequest(query, routingResolver.getRouting(),
entityType, index);
return doDeleteBy(prepareDeleteByRequest(request));
});
}
/**
* Customization hook on the actual execution result {@link Publisher}. <br />
*
* @param request the already prepared {@link DeleteRequest} ready to be executed.
* @return a {@link Mono} emitting the result of the operation.
*/
protected Mono<String> doDelete(DeleteRequest request) {
return Mono.from(execute(client -> client.delete(request))) //
.flatMap(it -> {
if (HttpStatus.valueOf(it.status().getStatus()).equals(HttpStatus.NOT_FOUND)) {
return Mono.empty();
}
return Mono.just(it.getId());
}) //
.onErrorResume(NoSuchIndexException.class, it -> Mono.empty());
}
/**
* Customization hook on the actual execution result {@link Publisher}. <br />
*
* @param request the already prepared {@link DeleteByQueryRequest} ready to be executed.
* @return a {@link Mono} emitting the result of the operation.
*/
protected Mono<BulkByScrollResponse> doDeleteBy(DeleteByQueryRequest request) {
return Mono.from(execute(client -> client.deleteBy(request))) //
.onErrorResume(NoSuchIndexException.class, it -> Mono.empty());
}
/**
* Customization hook to modify a generated {@link DeleteRequest} prior to its execution. E.g. by setting the
* {@link WriteRequest#setRefreshPolicy(String) refresh policy} if applicable.
*
* @param request the generated {@link DeleteRequest}.
* @return never {@literal null}.
*/
protected DeleteRequest prepareDeleteRequest(DeleteRequest request) {
return prepareWriteRequest(request);
}
/**
* Customization hook to modify a generated {@link DeleteByQueryRequest} prior to its execution. E.g. by setting the
* {@link WriteRequest#setRefreshPolicy(String) refresh policy} if applicable.
*
* @param request the generated {@link DeleteByQueryRequest}.
* @return never {@literal null}.
*/
protected DeleteByQueryRequest prepareDeleteByRequest(DeleteByQueryRequest request) {
if (refreshPolicy != null) {
if (RefreshPolicy.NONE.equals(refreshPolicy)) {
request = request.setRefresh(false);
} else {
request = request.setRefresh(true);
}
}
if (indicesOptions != null) {
request = request.setIndicesOptions(indicesOptions);
}
return request;
}
/**
* Customization hook to modify a generated {@link IndexRequest} prior to its execution. E.g. by setting the
* {@link WriteRequest#setRefreshPolicy(String) refresh policy} if applicable.
*
* @param source the source object the {@link IndexRequest} was derived from.
* @param request the generated {@link IndexRequest}.
* @return never {@literal null}.
*/
protected IndexRequest prepareIndexRequest(Object source, IndexRequest request) {
return prepareWriteRequest(request);
}
/**
* Preprocess the write request before it is sent to the server, e.g. by setting the
* {@link WriteRequest#setRefreshPolicy(String) refresh policy} if applicable.
*
* @param request must not be {@literal null}.
* @param <R>
* @return the processed {@link WriteRequest}.
*/
protected <R extends WriteRequest<R>> R prepareWriteRequest(R request) {
if (refreshPolicy == null) {
return request;
}
return request.setRefreshPolicy(RequestFactory.toElasticsearchRefreshPolicy(refreshPolicy));
}
// endregion
// region SearchOperations
@Override
public <T> Mono<SearchPage<T>> searchForPage(Query query, Class<?> entityType, Class<T> resultType) {
return searchForPage(query, entityType, resultType, getIndexCoordinatesFor(entityType));
}
@Override
public <T> Mono<SearchPage<T>> searchForPage(Query query, Class<?> entityType, Class<T> resultType,
IndexCoordinates index) {
SearchDocumentCallback<T> callback = new ReadSearchDocumentCallback<>(resultType, index);
return doFindForResponse(query, entityType, index) //
.flatMap(searchDocumentResponse -> Flux.fromIterable(searchDocumentResponse.getSearchDocuments()) //
.flatMap(callback::toEntity) //
.collectList() //
.map(entities -> SearchHitMapping.mappingFor(resultType, converter) //
.mapHits(searchDocumentResponse, entities))) //
.map(searchHits -> SearchHitSupport.searchPageFor(searchHits, query.getPageable()));
}
protected Flux<SearchDocument> doFind(Query query, Class<?> clazz, IndexCoordinates index) {
return Flux.defer(() -> {
SearchRequest request = requestFactory.searchRequest(query, routingResolver.getRouting(), clazz, index);
boolean useScroll = !(query.getPageable().isPaged() || query.isLimiting());
request = prepareSearchRequest(request, useScroll);
if (useScroll) {
return doScroll(request);
} else {
return doFind(request);
}
});
}
protected <T> Mono<SearchDocumentResponse> doFindForResponse(Query query, Class<?> clazz, IndexCoordinates index) {
return Mono.defer(() -> {
SearchRequest request = requestFactory.searchRequest(query, routingResolver.getRouting(), clazz, index);
request = prepareSearchRequest(request, false);
SearchDocumentCallback<?> documentCallback = new ReadSearchDocumentCallback<>(clazz, index);
// noinspection unchecked
SearchDocumentResponse.EntityCreator<T> entityCreator = searchDocument -> ((Mono<T>) documentCallback
.toEntity(searchDocument)).toFuture();
return doFindForResponse(request, entityCreator);
});
}
@Override
public Flux<AggregationContainer<?>> aggregate(Query query, Class<?> entityType, IndexCoordinates index) {
Assert.notNull(query, "query must not be null");
Assert.notNull(entityType, "entityType must not be null");
Assert.notNull(index, "index must not be null");
return Flux.defer(() -> {
SearchRequest request = requestFactory.searchRequest(query, routingResolver.getRouting(), entityType, index);
request = prepareSearchRequest(request, false);
return doAggregate(request);
});
}
/**
* Customization hook on the actual execution result {@link Publisher}. <br />
*
* @param request the already prepared {@link SearchRequest} ready to be executed.
* @return a {@link Flux} emitting the result of the operation.
*/
protected Flux<AggregationContainer<?>> doAggregate(SearchRequest request) {
if (QUERY_LOGGER.isDebugEnabled()) {
QUERY_LOGGER.debug(String.format("Executing doCount: %s", request));
}
return Flux.from(execute(client -> client.aggregate(request))) //
.onErrorResume(NoSuchIndexException.class, it -> Flux.empty()).map(ElasticsearchAggregation::new);
}
protected Mono<Long> doCount(Query query, Class<?> entityType, IndexCoordinates index) {
return Mono.defer(() -> {
SearchRequest request = requestFactory.searchRequest(query, routingResolver.getRouting(), entityType, index);
request = prepareSearchRequest(request, false);
return doCount(request);
});
}
/**
* Customization hook on the actual execution result {@link Publisher}. <br />
*
* @param request the already prepared {@link SearchRequest} ready to be executed.
* @return a {@link Flux} emitting the result of the operation converted to {@link SearchDocument}s.
*/
protected Flux<SearchDocument> doFind(SearchRequest request) {
if (QUERY_LOGGER.isDebugEnabled()) {
QUERY_LOGGER.debug(String.format("Executing doFind: %s", request));
}
return Flux.from(execute(client -> client.search(request))).map(DocumentAdapters::from) //
.onErrorResume(NoSuchIndexException.class, it -> Mono.empty());
}
/**
* Customization hook on the actual execution result {@link Mono}. <br />
*
* @param request the already prepared {@link SearchRequest} ready to be executed.
* @param entityCreator
* @return a {@link Mono} emitting the result of the operation converted to s {@link SearchDocumentResponse}.
*/
protected <T> Mono<SearchDocumentResponse> doFindForResponse(SearchRequest request,
SearchDocumentResponse.EntityCreator<T> entityCreator) {
if (QUERY_LOGGER.isDebugEnabled()) {
QUERY_LOGGER.debug(String.format("Executing doFindForResponse: %s", request));
}
return Mono.from(execute(client -> client.searchForResponse(request)))
.map(searchResponse -> SearchDocumentResponseBuilder.from(searchResponse, entityCreator));
}
/**
* Customization hook on the actual execution result {@link Publisher}. <br />
*
* @param request the already prepared {@link SearchRequest} ready to be executed.
* @return a {@link Mono} emitting the result of the operation.
*/
protected Mono<Long> doCount(SearchRequest request) {
if (QUERY_LOGGER.isDebugEnabled()) {
QUERY_LOGGER.debug(String.format("Executing doCount: %s", request));
}
return Mono.from(execute(client -> client.count(request)));
}
/**
* Customization hook on the actual execution result {@link Publisher}. <br />
*
* @param request the already prepared {@link SearchRequest} ready to be executed.
* @return a {@link Flux} emitting the result of the operation converted to {@link SearchDocument}s.
*/
protected Flux<SearchDocument> doScroll(SearchRequest request) {
if (QUERY_LOGGER.isDebugEnabled()) {
QUERY_LOGGER.debug(String.format("Executing doScroll: %s", request));
}
return Flux.from(execute(client -> client.scroll(request))) //
.map(DocumentAdapters::from).onErrorResume(NoSuchIndexException.class, it -> Mono.empty());
}
/**
* Customization hook to modify a generated {@link SearchRequest} prior to its execution. E.g. by setting the
* {@link SearchRequest#indicesOptions(IndicesOptions) indices options} if applicable.
*
* @param request the generated {@link SearchRequest}.
* @param useScroll
* @return never {@literal null}.
*/
protected SearchRequest prepareSearchRequest(SearchRequest request, boolean useScroll) {
if (indicesOptions != null) {
request = request.indicesOptions(indicesOptions);
}
// request_cache is not allowed on scroll requests.
if (useScroll) {
request = request.requestCache(null);
}
return request;
}
// endregion
// region Helper methods
@Override
public Mono<String> getClusterVersion() {
try {
return Mono.from(execute(ReactiveElasticsearchClient::info))
.map(mainResponse -> mainResponse.getVersion().toString());
} catch (Exception ignored) {}
return Mono.empty();
}
/**
* @return the vendor name of the used cluster and client library
* @since 4.3
*/
@Override
public Mono<String> getVendor() {
return Mono.just("Elasticsearch");
}
/**
* @return the version of the used client runtime library.
* @since 4.3
*/
@Override
public Mono<String> getRuntimeLibraryVersion() {
return Mono.just(Version.CURRENT.toString());
}
@Override
public Query matchAllQuery() {
return new NativeSearchQueryBuilder().withQuery(QueryBuilders.matchAllQuery()).build();
}
@Override
public Query idsQuery(List<String> ids) {
Assert.notNull(ids, "ids must not be null");
return new NativeSearchQueryBuilder().withQuery(QueryBuilders.idsQuery().addIds(ids.toArray(new String[] {})))
.build();
}
// endregion
@Override
public <T> Publisher<T> execute(ClientCallback<Publisher<T>> callback) {
return Flux.defer(() -> callback.doWithClient(getClient())).onErrorMap(this::translateException);
}
@Override
public <T> Publisher<T> executeWithIndicesClient(IndicesClientCallback<Publisher<T>> callback) {
return Flux.defer(() -> callback.doWithClient(getIndicesClient())).onErrorMap(this::translateException);
}
@Override
public <T> Publisher<T> executeWithClusterClient(ClusterClientCallback<Publisher<T>> callback) {
return Flux.defer(() -> callback.doWithClient(getClusterClient())).onErrorMap(this::translateException);
}
@Override
public ReactiveIndexOperations indexOps(IndexCoordinates index) {
return new ReactiveIndexTemplate(this, index);
}
@Override
public ReactiveIndexOperations indexOps(Class<?> clazz) {
return new ReactiveIndexTemplate(this, clazz);
}
@Override
public ReactiveClusterOperations cluster() {
return new DefaultReactiveClusterOperations(this);
}
/**
* Obtain the {@link ReactiveElasticsearchClient} to operate upon.
*
* @return never {@literal null}.
*/
protected ReactiveElasticsearchClient getClient() {
return this.client;
}
/**
* Obtain the {@link ReactiveElasticsearchClient.Indices} to operate upon.
*
* @return never {@literal null}.
*/
protected ReactiveElasticsearchClient.Indices getIndicesClient() {
if (client instanceof ReactiveElasticsearchClient.Indices) {
return (ReactiveElasticsearchClient.Indices) client;
}
throw new UncategorizedElasticsearchException("No ReactiveElasticsearchClient.Indices implementation available");
}
/**
* Obtain the {@link ReactiveElasticsearchClient.Cluster} to operate upon.
*
* @return never {@literal null}.
*/
protected ReactiveElasticsearchClient.Cluster getClusterClient() {
if (client instanceof ReactiveElasticsearchClient.Cluster) {
return (ReactiveElasticsearchClient.Cluster) client;
}
throw new UncategorizedElasticsearchException("No ReactiveElasticsearchClient.Cluster implementation available");
}
/**
* translates an Exception if possible. Exceptions that are no {@link RuntimeException}s are wrapped in a
* RuntimeException
*
* @param throwable the Throwable to map
* @return the potentially translated RuntimeException.
* @since 4.0
*/
private RuntimeException translateException(Throwable throwable) {
RuntimeException runtimeException = throwable instanceof RuntimeException ? (RuntimeException) throwable
: new RuntimeException(throwable.getMessage(), throwable);
RuntimeException potentiallyTranslatedException = exceptionTranslator
.translateExceptionIfPossible(runtimeException);
return potentiallyTranslatedException != null ? potentiallyTranslatedException : runtimeException;
}
}

View File

@ -1,402 +0,0 @@
/*
* Copyright 2020-2023 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.erhlc;
import static org.elasticsearch.client.Requests.refreshRequest;
import static org.springframework.util.StringUtils.hasText;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.util.Map;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
import org.elasticsearch.action.admin.indices.alias.get.GetAliasesRequest;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.admin.indices.settings.get.GetSettingsRequest;
import org.elasticsearch.action.admin.indices.template.delete.DeleteIndexTemplateRequest;
import org.elasticsearch.client.GetAliasesResponse;
import org.elasticsearch.client.indices.*;
import org.elasticsearch.client.indices.PutIndexTemplateRequest;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.elasticsearch.NoSuchIndexException;
import org.springframework.data.elasticsearch.annotations.Mapping;
import org.springframework.data.elasticsearch.core.IndexInformation;
import org.springframework.data.elasticsearch.core.ReactiveElasticsearchOperations;
import org.springframework.data.elasticsearch.core.ReactiveIndexOperations;
import org.springframework.data.elasticsearch.core.ReactiveResourceUtil;
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
import org.springframework.data.elasticsearch.core.document.Document;
import org.springframework.data.elasticsearch.core.index.*;
import org.springframework.data.elasticsearch.core.index.DeleteComponentTemplateRequest;
import org.springframework.data.elasticsearch.core.index.PutComponentTemplateRequest;
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentEntity;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
* @author Peter-Josef Meisch
* @author George Popides
* @since 4.1
* @deprecated since 5.0
*/
@Deprecated
class ReactiveIndexTemplate implements ReactiveIndexOperations {
private static final Log LOGGER = LogFactory.getLog(ReactiveIndexTemplate.class);
@Nullable private final Class<?> boundClass;
private final IndexCoordinates boundIndex;
private final RequestFactory requestFactory;
private final ReactiveElasticsearchOperations operations;
private final ElasticsearchConverter converter;
public ReactiveIndexTemplate(ReactiveElasticsearchOperations operations, IndexCoordinates index) {
Assert.notNull(operations, "operations must not be null");
Assert.notNull(index, "index must not be null");
this.operations = operations;
this.converter = operations.getElasticsearchConverter();
this.requestFactory = new RequestFactory(operations.getElasticsearchConverter());
this.boundClass = null;
this.boundIndex = index;
}
public ReactiveIndexTemplate(ReactiveElasticsearchOperations operations, Class<?> clazz) {
Assert.notNull(operations, "operations must not be null");
Assert.notNull(clazz, "clazz must not be null");
this.operations = operations;
this.converter = operations.getElasticsearchConverter();
this.requestFactory = new RequestFactory(operations.getElasticsearchConverter());
this.boundClass = clazz;
this.boundIndex = getIndexCoordinatesFor(clazz);
}
// region index management
@Override
public Mono<Boolean> create() {
IndexCoordinates index = getIndexCoordinates();
if (boundClass != null) {
return createSettings(boundClass).flatMap(settings -> doCreate(index, settings, null));
} else {
return doCreate(index, new Settings(), null);
}
}
@Override
public Mono<Boolean> createWithMapping() {
return createSettings() //
.flatMap(settings -> //
createMapping().flatMap(mapping -> //
doCreate(getIndexCoordinates(), settings, mapping))); //
}
@Override
public Mono<Boolean> create(Map<String, Object> settings) {
Assert.notNull(settings, "settings must not be null");
return doCreate(getIndexCoordinates(), settings, null);
}
@Override
public Mono<Boolean> create(Map<String, Object> settings, Document mapping) {
Assert.notNull(settings, "settings must not be null");
Assert.notNull(mapping, "mapping must not be null");
return doCreate(getIndexCoordinates(), settings, mapping);
}
private Mono<Boolean> doCreate(IndexCoordinates index, Map<String, Object> settings, @Nullable Document mapping) {
CreateIndexRequest request = requestFactory.createIndexRequest(index, settings, mapping);
return Mono.from(operations.executeWithIndicesClient(client -> client.createIndex(request)));
}
@Override
public Mono<Boolean> delete() {
return exists() //
.flatMap(exists -> {
if (exists) {
DeleteIndexRequest request = requestFactory.deleteIndexRequest(getIndexCoordinates());
return Mono.from(operations.executeWithIndicesClient(client -> client.deleteIndex(request)))
.onErrorResume(NoSuchIndexException.class, e -> Mono.just(false));
} else {
return Mono.just(false);
}
});
}
@Override
public Mono<Boolean> exists() {
GetIndexRequest request = requestFactory.getIndexRequest(getIndexCoordinates());
return Mono.from(operations.executeWithIndicesClient(client -> client.existsIndex(request)));
}
@Override
public Mono<Void> refresh() {
return Mono.from(operations.executeWithIndicesClient(
client -> client.refreshIndex(refreshRequest(getIndexCoordinates().getIndexNames()))));
}
// endregion
// region mappings
@Override
public Mono<Document> createMapping() {
return createMapping(checkForBoundClass());
}
@Override
public Mono<Document> createMapping(Class<?> clazz) {
// noinspection DuplicatedCode
Mapping mappingAnnotation = AnnotatedElementUtils.findMergedAnnotation(clazz, Mapping.class);
if (mappingAnnotation != null) {
String mappingPath = mappingAnnotation.mappingPath();
if (hasText(mappingPath)) {
return ReactiveResourceUtil.loadDocument(mappingAnnotation.mappingPath(), "@Mapping");
}
}
return new ReactiveMappingBuilder(converter).buildReactivePropertyMapping(clazz).map(Document::parse);
}
@Override
public Mono<Boolean> putMapping(Mono<Document> mapping) {
return mapping.map(document -> requestFactory.putMappingRequest(getIndexCoordinates(), document)) //
.flatMap(request -> Mono.from(operations.executeWithIndicesClient(client -> client.putMapping(request))));
}
@Override
public Mono<Document> getMapping() {
IndexCoordinates indexCoordinates = getIndexCoordinates();
GetMappingsRequest request = requestFactory.getMappingsRequest(indexCoordinates);
return Mono.from(operations.executeWithIndicesClient(client -> client.getMapping(request)))
.flatMap(getMappingsResponse -> {
Map<String, Object> source = getMappingsResponse.mappings().get(indexCoordinates.getIndexName())
.getSourceAsMap();
Document document = Document.from(source);
return Mono.just(document);
});
}
// endregion
// region settings
@Override
public Mono<Settings> createSettings() {
return createSettings(checkForBoundClass());
}
@Override
public Mono<Settings> createSettings(Class<?> clazz) {
Assert.notNull(clazz, "clazz must not be null");
ElasticsearchPersistentEntity<?> persistentEntity = getRequiredPersistentEntity(clazz);
String settingPath = persistentEntity.settingPath();
return hasText(settingPath) //
? ReactiveResourceUtil.loadDocument(settingPath, "@Setting") //
.map(Settings::new) //
: Mono.just(persistentEntity.getDefaultSettings());
}
@Override
public Mono<Settings> getSettings(boolean includeDefaults) {
String indexName = getIndexCoordinates().getIndexName();
GetSettingsRequest request = requestFactory.getSettingsRequest(indexName, includeDefaults);
return Mono.from(operations.executeWithIndicesClient(client -> client.getSettings(request)))
.map(getSettingsResponse -> ResponseConverter.fromSettingsResponse(getSettingsResponse, indexName));
}
// endregion
// region aliases
@Override
public Mono<Boolean> alias(AliasActions aliasActions) {
IndicesAliasesRequest request = requestFactory.indicesAliasesRequest(aliasActions);
return Mono.from(operations.executeWithIndicesClient(client -> client.updateAliases(request)));
}
@Override
public Mono<Map<String, Set<AliasData>>> getAliases(String... aliasNames) {
return getAliases(aliasNames, null);
}
@Override
public Mono<Map<String, Set<AliasData>>> getAliasesForIndex(String... indexNames) {
return getAliases(null, indexNames);
}
private Mono<Map<String, Set<AliasData>>> getAliases(@Nullable String[] aliasNames, @Nullable String[] indexNames) {
GetAliasesRequest getAliasesRequest = requestFactory.getAliasesRequest(aliasNames, indexNames);
return Mono.from(operations.executeWithIndicesClient(client -> client.getAliases(getAliasesRequest)))
.map(GetAliasesResponse::getAliases).map(ResponseConverter::aliasDatas);
}
// endregion
// region templates
@Override
public Mono<Boolean> putTemplate(PutTemplateRequest putTemplateRequest) {
Assert.notNull(putTemplateRequest, "putTemplateRequest must not be null");
PutIndexTemplateRequest putIndexTemplateRequest = requestFactory.putIndexTemplateRequest(putTemplateRequest);
return Mono.from(operations.executeWithIndicesClient(client -> client.putTemplate(putIndexTemplateRequest)));
}
@Override
public Mono<Boolean> putComponentTemplate(PutComponentTemplateRequest putComponentTemplateRequest) {
throw new UnsupportedOperationException("not implemented");
}
@Override
public Flux<TemplateResponse> getComponentTemplate(GetComponentTemplateRequest request) {
throw new UnsupportedOperationException("not implemented");
}
@Override
public Mono<Boolean> existsComponentTemplate(ExistsComponentTemplateRequest existsComponentTemplateRequest) {
throw new UnsupportedOperationException("not implemented");
}
@Override
public Mono<Boolean> deleteComponentTemplate(DeleteComponentTemplateRequest deleteComponentTemplateRequest) {
throw new UnsupportedOperationException("not implemented");
}
@Override
public Mono<Boolean> putIndexTemplate(
org.springframework.data.elasticsearch.core.index.PutIndexTemplateRequest putIndexTemplateRequest) {
throw new UnsupportedOperationException("not implemented");
}
@Override
public Mono<Boolean> existsIndexTemplate(ExistsIndexTemplateRequest existsIndexTemplateRequest) {
throw new UnsupportedOperationException("not implemented");
}
@Override
public Flux<TemplateResponse> getIndexTemplate(GetIndexTemplateRequest getIndexTemplateRequest) {
throw new UnsupportedOperationException("not implemented");
}
@Override
public Mono<Boolean> deleteIndexTemplate(
org.springframework.data.elasticsearch.core.index.DeleteIndexTemplateRequest deleteIndexTemplateRequest) {
throw new UnsupportedOperationException("not implemented");
}
@Override
public Mono<TemplateData> getTemplate(GetTemplateRequest getTemplateRequest) {
Assert.notNull(getTemplateRequest, "getTemplateRequest must not be null");
GetIndexTemplatesRequest getIndexTemplatesRequest = requestFactory.getIndexTemplatesRequest(getTemplateRequest);
return Mono.from(operations.executeWithIndicesClient(client -> client.getTemplate(getIndexTemplatesRequest)))
.flatMap(response -> {
if (response != null) {
TemplateData templateData = ResponseConverter.getTemplateData(response,
getTemplateRequest.getTemplateName());
if (templateData != null) {
return Mono.just(templateData);
}
}
return Mono.empty();
});
}
@Override
public Mono<Boolean> existsTemplate(ExistsTemplateRequest existsTemplateRequest) {
Assert.notNull(existsTemplateRequest, "existsTemplateRequest must not be null");
IndexTemplatesExistRequest indexTemplatesExistRequest = requestFactory
.indexTemplatesExistsRequest(existsTemplateRequest);
return Mono.from(operations.executeWithIndicesClient(client -> client.existsTemplate(indexTemplatesExistRequest)));
}
@Override
public Mono<Boolean> deleteTemplate(DeleteTemplateRequest deleteTemplateRequest) {
Assert.notNull(deleteTemplateRequest, "deleteTemplateRequest must not be null");
DeleteIndexTemplateRequest deleteIndexTemplateRequest = requestFactory
.deleteIndexTemplateRequest(deleteTemplateRequest);
return Mono.from(operations.executeWithIndicesClient(client -> client.deleteTemplate(deleteIndexTemplateRequest)));
}
// endregion
// region helper functions
@Override
public IndexCoordinates getIndexCoordinates() {
return (boundClass != null) ? getIndexCoordinatesFor(boundClass) : boundIndex;
}
@Override
public Flux<IndexInformation> getInformation(IndexCoordinates index) {
Assert.notNull(index, "index must not be null");
org.elasticsearch.client.indices.GetIndexRequest getIndexRequest = requestFactory.getIndexRequest(index);
return Mono
.from(operations.executeWithIndicesClient(
client -> client.getIndex(getIndexRequest).map(ResponseConverter::getIndexInformations)))
.flatMapMany(Flux::fromIterable);
}
private IndexCoordinates getIndexCoordinatesFor(Class<?> clazz) {
return operations.getElasticsearchConverter().getMappingContext().getRequiredPersistentEntity(clazz)
.getIndexCoordinates();
}
private ElasticsearchPersistentEntity<?> getRequiredPersistentEntity(Class<?> clazz) {
return converter.getMappingContext().getRequiredPersistentEntity(clazz);
}
private Class<?> checkForBoundClass() {
if (boundClass == null) {
throw new InvalidDataAccessApiUsageException("IndexOperations are not bound");
}
return boundClass;
}
// endregion
}

View File

@ -1,88 +0,0 @@
/*
* Copyright 2018-2023 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.erhlc;
import java.util.function.Function;
import org.springframework.data.elasticsearch.client.ClientConfiguration;
import org.springframework.util.Assert;
import org.springframework.web.reactive.function.client.WebClient;
/**
* Utility class for common access to reactive Elasticsearch clients. {@link ReactiveRestClients} consolidates set up
* routines for the various drivers into a single place.
*
* @author Christoph Strobl
* @author Mark Paluch
* @author Roman Puchkovskiy
* @since 3.2
* @deprecated since 5.0
*/
@Deprecated
public final class ReactiveRestClients {
private ReactiveRestClients() {}
/**
* Start here to create a new client tailored to your needs.
*
* @param clientConfiguration client configuration to use for building {@link ReactiveElasticsearchClient}; must not
* be {@literal null}.
* @return new instance of {@link ReactiveElasticsearchClient}.
*/
public static ReactiveElasticsearchClient create(ClientConfiguration clientConfiguration) {
Assert.notNull(clientConfiguration, "ClientConfiguration must not be null!");
return DefaultReactiveElasticsearchClient.create(clientConfiguration);
}
/**
* Start here to create a new client tailored to your needs.
*
* @param clientConfiguration client configuration to use for building {@link ReactiveElasticsearchClient}; must not
* be {@literal null}.
* @param requestCreator request creator to use in the client; must not be {@literal null}.
* @return new instance of {@link ReactiveElasticsearchClient}.
*/
public static ReactiveElasticsearchClient create(ClientConfiguration clientConfiguration,
RequestCreator requestCreator) {
Assert.notNull(clientConfiguration, "ClientConfiguration must not be null!");
Assert.notNull(requestCreator, "RequestCreator must not be null!");
return DefaultReactiveElasticsearchClient.create(clientConfiguration, requestCreator);
}
/**
* {@link org.springframework.data.elasticsearch.client.ClientConfiguration.ClientConfigurationCallback} to configure
* the ReactiveElasticsearchClient with a {@link WebClient}
*
* @since 4.3
* @deprecated
*/
@Deprecated
public interface WebClientConfigurationCallback extends ClientConfiguration.ClientConfigurationCallback<WebClient> {
static WebClientConfigurationCallback from(Function<WebClient, WebClient> webClientCallback) {
Assert.notNull(webClientCallback, "webClientCallback must not be null");
// noinspection NullableProblems
return webClientCallback::apply;
}
}
}

View File

@ -1,42 +0,0 @@
/*
* Copyright 2018-2023 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.erhlc;
import org.springframework.web.reactive.function.client.WebClientException;
/**
* Exception thrown if the request body cannot be properly encoded.
*
* @author Christoph Strobl
* @author Mark Paluch
* @since 3.2
* @deprecated since 5.0
*/
@Deprecated public class RequestBodyEncodingException extends WebClientException {
private static final long serialVersionUID = 472776714118912855L;
/**
* Construct a new instance of {@link RequestBodyEncodingException} with the given message and exception.
*
* @param msg the message
* @param ex the exception
*/
public RequestBodyEncodingException(String msg, Throwable ex) {
super(msg, ex);
}
}

View File

@ -1,308 +0,0 @@
/*
* Copyright 2021-2023 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.erhlc;
import java.io.IOException;
import java.util.function.Function;
import org.elasticsearch.action.admin.cluster.health.ClusterHealthRequest;
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
import org.elasticsearch.action.admin.indices.alias.get.GetAliasesRequest;
import org.elasticsearch.action.admin.indices.close.CloseIndexRequest;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.admin.indices.flush.FlushRequest;
import org.elasticsearch.action.admin.indices.open.OpenIndexRequest;
import org.elasticsearch.action.admin.indices.refresh.RefreshRequest;
import org.elasticsearch.action.admin.indices.settings.get.GetSettingsRequest;
import org.elasticsearch.action.admin.indices.template.delete.DeleteIndexTemplateRequest;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.MultiGetRequest;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.main.MainRequest;
import org.elasticsearch.action.search.ClearScrollRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchScrollRequest;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.client.Request;
import org.elasticsearch.client.core.CountRequest;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.GetFieldMappingsRequest;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.client.indices.GetIndexTemplatesRequest;
import org.elasticsearch.client.indices.GetMappingsRequest;
import org.elasticsearch.client.indices.IndexTemplatesExistRequest;
import org.elasticsearch.client.indices.PutIndexTemplateRequest;
import org.elasticsearch.client.indices.PutMappingRequest;
import org.elasticsearch.index.reindex.DeleteByQueryRequest;
import org.elasticsearch.index.reindex.ReindexRequest;
import org.elasticsearch.index.reindex.UpdateByQueryRequest;
import org.elasticsearch.script.mustache.SearchTemplateRequest;
import org.springframework.data.elasticsearch.UncategorizedElasticsearchException;
/**
* @author Roman Puchkovskiy
* @author Farid Faoudi
* @author George Popides
* @since 4.0
* @deprecated since 5.0
*/
@Deprecated
public interface RequestCreator {
default Function<SearchRequest, Request> search() {
return RequestConverters::search;
}
default Function<SearchTemplateRequest, Request> searchTemplate() {
return RequestConverters::searchTemplate;
}
default Function<SearchScrollRequest, Request> scroll() {
return RequestConverters::searchScroll;
}
default Function<ClearScrollRequest, Request> clearScroll() {
return RequestConverters::clearScroll;
}
default Function<IndexRequest, Request> index() {
return RequestConverters::index;
}
default Function<GetRequest, Request> get() {
return RequestConverters::get;
}
default Function<MainRequest, Request> ping() {
return (request) -> RequestConverters.ping();
}
default Function<MainRequest, Request> info() {
return (request) -> RequestConverters.info();
}
default Function<MultiGetRequest, Request> multiGet() {
return RequestConverters::multiGet;
}
default Function<GetRequest, Request> exists() {
return RequestConverters::exists;
}
default Function<UpdateRequest, Request> update() {
return RequestConverters::update;
}
default Function<DeleteRequest, Request> delete() {
return RequestConverters::delete;
}
default Function<DeleteByQueryRequest, Request> deleteByQuery() {
return RequestConverters::deleteByQuery;
}
/**
* @since 4.2
*/
default Function<UpdateByQueryRequest, Request> updateByQuery() {
return RequestConverters::updateByQuery;
}
default Function<BulkRequest, Request> bulk() {
return request -> {
try {
return RequestConverters.bulk(request);
} catch (IOException e) {
throw new UncategorizedElasticsearchException("Could not parse request", e);
}
};
}
// --> INDICES
/**
* @deprecated since 4.2
*/
@Deprecated
default Function<org.elasticsearch.action.admin.indices.get.GetIndexRequest, Request> indexExists() {
return RequestConverters::indexExists;
}
/**
* @since 4.2
*/
default Function<GetIndexRequest, Request> indexExistsRequest() {
return RequestConverters::indexExists;
}
default Function<DeleteIndexRequest, Request> indexDelete() {
return RequestConverters::indexDelete;
}
/**
* @deprecated since 4.2
*/
@Deprecated
default Function<org.elasticsearch.action.admin.indices.create.CreateIndexRequest, Request> indexCreate() {
return RequestConverters::indexCreate;
}
/**
* @since 4.2
*/
default Function<CreateIndexRequest, Request> createIndexRequest() {
return RequestConverters::indexCreate;
}
default Function<OpenIndexRequest, Request> indexOpen() {
return RequestConverters::indexOpen;
}
default Function<CloseIndexRequest, Request> indexClose() {
return RequestConverters::indexClose;
}
default Function<RefreshRequest, Request> indexRefresh() {
return RequestConverters::indexRefresh;
}
/**
* @deprecated since 4.2
*/
@Deprecated
default Function<org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest, Request> putMapping() {
return RequestConverters::putMapping;
}
/**
* @since 4.2
*/
default Function<PutMappingRequest, Request> putMappingRequest() {
return RequestConverters::putMapping;
}
default Function<FlushRequest, Request> flushIndex() {
return RequestConverters::flushIndex;
}
default Function<CountRequest, Request> count() {
return RequestConverters::count;
}
/**
* @since 4.1
*/
default Function<GetSettingsRequest, Request> getSettings() {
return RequestConverters::getSettings;
}
/**
* @since 4.1
* @deprecated since 4.2
*/
@Deprecated
default Function<org.elasticsearch.action.admin.indices.mapping.get.GetMappingsRequest, Request> getMapping() {
return RequestConverters::getMapping;
}
/**
* @since 4.2
*/
default Function<GetMappingsRequest, Request> getMappingRequest() {
return RequestConverters::getMapping;
}
/**
* @since 4.1
*/
default Function<IndicesAliasesRequest, Request> updateAlias() {
return RequestConverters::updateAliases;
}
/**
* @since 4.1
*/
default Function<GetAliasesRequest, Request> getAlias() {
return RequestConverters::getAlias;
}
/**
* @since 4.1
*/
default Function<PutIndexTemplateRequest, Request> putTemplate() {
return RequestConverters::putTemplate;
}
/**
* @since 4.1
*/
default Function<GetIndexTemplatesRequest, Request> getTemplates() {
return RequestConverters::getTemplates;
}
/**
* @since 4.1
*/
default Function<IndexTemplatesExistRequest, Request> templatesExist() {
return RequestConverters::templatesExist;
}
/**
* @since 4.1
*/
default Function<DeleteIndexTemplateRequest, Request> deleteTemplate() {
return RequestConverters::deleteTemplate;
}
/**
* @since 4.2
*/
default Function<GetFieldMappingsRequest, Request> getFieldMapping() {
return RequestConverters::getFieldMapping;
}
/**
* @since 4.2
*/
default Function<GetIndexRequest, Request> getIndex() {
return RequestConverters::getIndex;
}
/**
* @since 4.2
*/
default Function<ClusterHealthRequest, Request> clusterHealth() {
return RequestConverters::clusterHealth;
}
/**
* @since 4.4
*/
default Function<ReindexRequest, Request> reindex() {
return RequestConverters::reindex;
}
/**
* @since 4.4
*/
default Function<ReindexRequest, Request> submitReindex() {
return RequestConverters::submitReindex;
}
}

View File

@ -1,441 +0,0 @@
/*
* Copyright 2021-2023 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.erhlc;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse;
import org.elasticsearch.action.admin.indices.settings.get.GetSettingsResponse;
import org.elasticsearch.action.bulk.BulkItemResponse;
import org.elasticsearch.action.get.MultiGetItemResponse;
import org.elasticsearch.action.get.MultiGetResponse;
import org.elasticsearch.client.indices.GetIndexResponse;
import org.elasticsearch.client.indices.GetIndexTemplatesResponse;
import org.elasticsearch.client.indices.IndexTemplateMetadata;
import org.elasticsearch.cluster.metadata.AliasMetadata;
import org.elasticsearch.cluster.metadata.MappingMetadata;
import org.elasticsearch.common.collect.ImmutableOpenMap;
import org.elasticsearch.common.compress.CompressedXContent;
import org.elasticsearch.index.reindex.BulkByScrollResponse;
import org.elasticsearch.index.reindex.ScrollableHitSource;
import org.springframework.data.elasticsearch.core.IndexInformation;
import org.springframework.data.elasticsearch.core.MultiGetItem;
import org.springframework.data.elasticsearch.core.cluster.ClusterHealth;
import org.springframework.data.elasticsearch.core.document.Document;
import org.springframework.data.elasticsearch.core.index.AliasData;
import org.springframework.data.elasticsearch.core.index.Settings;
import org.springframework.data.elasticsearch.core.index.TemplateData;
import org.springframework.data.elasticsearch.core.query.ByQueryResponse;
import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.data.elasticsearch.core.query.StringQuery;
import org.springframework.data.elasticsearch.core.reindex.ReindexResponse;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
* Factory class to convert elasticsearch responses to different type of data classes.
*
* @author George Popides
* @author Peter-Josef Meisch
* @author Sijia Liu
* @since 4.2
* @deprecated since 5.0
*/
@Deprecated
public class ResponseConverter {
private ResponseConverter() {}
// region alias
public static Map<String, Set<AliasData>> aliasDatas(Map<String, Set<AliasMetadata>> aliasesMetadatas) {
Map<String, Set<AliasData>> converted = new LinkedHashMap<>();
aliasesMetadatas.forEach((index, aliasMetaDataSet) -> {
Set<AliasData> aliasDataSet = new LinkedHashSet<>();
aliasMetaDataSet.forEach(aliasMetaData -> aliasDataSet.add(toAliasData(aliasMetaData)));
converted.put(index, aliasDataSet);
});
return converted;
}
public static AliasData toAliasData(AliasMetadata aliasMetaData) {
CompressedXContent aliasMetaDataFilter = aliasMetaData.getFilter();
Query filterQuery = (aliasMetaDataFilter != null) ? StringQuery.builder(aliasMetaDataFilter.string()).build()
: null;
return AliasData.of(aliasMetaData.alias(), filterQuery, aliasMetaData.indexRouting(),
aliasMetaData.getSearchRouting(), aliasMetaData.writeIndex(), aliasMetaData.isHidden());
}
// endregion
// region index informations
/**
* get the index informations from a {@link GetIndexResponse}
*
* @param getIndexResponse the index response, must not be {@literal null}
* @return list of {@link IndexInformation}s for the different indices
*/
public static List<IndexInformation> getIndexInformations(GetIndexResponse getIndexResponse) {
Assert.notNull(getIndexResponse, "getIndexResponse must not be null");
List<IndexInformation> indexInformationList = new ArrayList<>();
for (String indexName : getIndexResponse.getIndices()) {
Settings settings = settingsFromGetIndexResponse(getIndexResponse, indexName);
Document mappings = mappingsFromGetIndexResponse(getIndexResponse, indexName);
List<AliasData> aliases = aliasDataFromIndexResponse(getIndexResponse, indexName);
indexInformationList.add(IndexInformation.of(indexName, settings, mappings, aliases));
}
return indexInformationList;
}
/**
* extract the index settings information from a given index
*
* @param getIndexResponse the elastic GetIndexResponse
* @param indexName the index name
* @return a document that represents {@link Settings}
*/
private static Settings settingsFromGetIndexResponse(GetIndexResponse getIndexResponse, String indexName) {
Settings settings = new Settings();
org.elasticsearch.common.settings.Settings indexSettings = getIndexResponse.getSettings().get(indexName);
if (!indexSettings.isEmpty()) {
for (String key : indexSettings.keySet()) {
settings.put(key, indexSettings.get(key));
}
}
return settings;
}
/**
* extract the mappings information from a given index
*
* @param getIndexResponse the elastic GetIndexResponse
* @param indexName the index name
* @return a document that represents {@link MappingMetadata}
*/
private static Document mappingsFromGetIndexResponse(GetIndexResponse getIndexResponse, String indexName) {
Document document = Document.create();
if (getIndexResponse.getMappings().containsKey(indexName)) {
MappingMetadata mappings = getIndexResponse.getMappings().get(indexName);
document = Document.from(mappings.getSourceAsMap());
}
return document;
}
private static List<AliasData> aliasDataFromIndexResponse(GetIndexResponse getIndexResponse, String indexName) {
List<AliasData> aliases = Collections.emptyList();
if (getIndexResponse.getAliases().get(indexName) != null) {
aliases = getIndexResponse.getAliases().get(indexName).stream().map(ResponseConverter::toAliasData)
.collect(Collectors.toList());
}
return aliases;
}
/**
* get the index informations from a {@link org.elasticsearch.action.admin.indices.get.GetIndexResponse} (transport
* client)
*
* @param getIndexResponse the index response, must not be {@literal null}
* @return list of {@link IndexInformation}s for the different indices
*/
public static List<IndexInformation> getIndexInformations(
org.elasticsearch.action.admin.indices.get.GetIndexResponse getIndexResponse) {
List<IndexInformation> indexInformationList = new ArrayList<>();
for (String indexName : getIndexResponse.getIndices()) {
Settings settings = settingsFromGetIndexResponse(getIndexResponse, indexName);
Document mappings = mappingsFromGetIndexResponse(getIndexResponse, indexName);
List<AliasData> aliases = aliasDataFromIndexResponse(getIndexResponse, indexName);
indexInformationList.add(IndexInformation.of(indexName, settings, mappings, aliases));
}
return indexInformationList;
}
private static Settings settingsFromGetIndexResponse(
org.elasticsearch.action.admin.indices.get.GetIndexResponse getIndexResponse, String indexName) {
Settings settings = new Settings();
if (getIndexResponse.getSettings().containsKey(indexName)) {
org.elasticsearch.common.settings.Settings indexSettings = getIndexResponse.getSettings().get(indexName);
for (String key : indexSettings.keySet()) {
settings.put(key, indexSettings.get(key));
}
}
return settings;
}
private static Document mappingsFromGetIndexResponse(
org.elasticsearch.action.admin.indices.get.GetIndexResponse getIndexResponse, String indexName) {
Document document = Document.create();
boolean responseHasMappings = getIndexResponse.getMappings().containsKey(indexName)
&& (getIndexResponse.getMappings().get(indexName).get("_doc") != null);
if (responseHasMappings) {
MappingMetadata mappings = getIndexResponse.getMappings().get(indexName).get("_doc");
document = Document.from(mappings.getSourceAsMap());
}
return document;
}
private static List<AliasData> aliasDataFromIndexResponse(
org.elasticsearch.action.admin.indices.get.GetIndexResponse getIndexResponse, String indexName) {
List<AliasData> aliases = Collections.emptyList();
if (getIndexResponse.getAliases().get(indexName) != null) {
aliases = getIndexResponse.getAliases().get(indexName).stream().map(ResponseConverter::toAliasData)
.collect(Collectors.toList());
}
return aliases;
}
// endregion
// region templates
@Nullable
public static TemplateData getTemplateData(GetIndexTemplatesResponse getIndexTemplatesResponse, String templateName) {
for (IndexTemplateMetadata indexTemplateMetadata : getIndexTemplatesResponse.getIndexTemplates()) {
if (indexTemplateMetadata.name().equals(templateName)) {
Settings settings = new Settings();
org.elasticsearch.common.settings.Settings templateSettings = indexTemplateMetadata.settings();
templateSettings.keySet().forEach(key -> settings.put(key, templateSettings.get(key)));
Map<String, AliasData> aliases = new LinkedHashMap<>();
ImmutableOpenMap<String, AliasMetadata> aliasesResponse = indexTemplateMetadata.aliases();
Iterator<String> keysIt = aliasesResponse.keysIt();
while (keysIt.hasNext()) {
String key = keysIt.next();
aliases.put(key, ResponseConverter.toAliasData(aliasesResponse.get(key)));
}
return TemplateData.builder().withIndexPatterns(indexTemplateMetadata.patterns().toArray(new String[0])) //
.withSettings(settings) //
.withMapping(Document.from(indexTemplateMetadata.mappings().getSourceAsMap())) //
.withAliases(aliases) //
.withOrder(indexTemplateMetadata.order()) //
.withVersion(indexTemplateMetadata.version()).build();
}
}
return null;
}
// endregion
// region settings
/**
* extract the index settings information for a given index
*
* @param response the Elasticsearch response
* @param indexName the index name
* @return settings
*/
public static Settings fromSettingsResponse(GetSettingsResponse response, String indexName) {
Settings settings = new Settings();
if (!response.getIndexToDefaultSettings().isEmpty()) {
org.elasticsearch.common.settings.Settings defaultSettings = response.getIndexToDefaultSettings().get(indexName);
for (String key : defaultSettings.keySet()) {
settings.put(key, defaultSettings.get(key));
}
}
if (!response.getIndexToSettings().isEmpty()) {
org.elasticsearch.common.settings.Settings customSettings = response.getIndexToSettings().get(indexName);
for (String key : customSettings.keySet()) {
settings.put(key, customSettings.get(key));
}
}
return settings;
}
// endregion
// region multiget
@Nullable
public static MultiGetItem.Failure getFailure(MultiGetItemResponse itemResponse) {
MultiGetResponse.Failure responseFailure = itemResponse.getFailure();
return responseFailure != null ? MultiGetItem.Failure.of(responseFailure.getIndex(), responseFailure.getType(),
responseFailure.getId(), responseFailure.getFailure(), null) : null;
}
// endregion
// region cluster operations
public static ClusterHealth clusterHealth(ClusterHealthResponse clusterHealthResponse) {
return ClusterHealth.builder() //
.withActivePrimaryShards(clusterHealthResponse.getActivePrimaryShards()) //
.withActiveShards(clusterHealthResponse.getActiveShards()) //
.withActiveShardsPercent(clusterHealthResponse.getActiveShardsPercent()) //
.withClusterName(clusterHealthResponse.getClusterName()) //
.withDelayedUnassignedShards(clusterHealthResponse.getDelayedUnassignedShards()) //
.withInitializingShards(clusterHealthResponse.getInitializingShards()) //
.withNumberOfDataNodes(clusterHealthResponse.getNumberOfDataNodes()) //
.withNumberOfInFlightFetch(clusterHealthResponse.getNumberOfInFlightFetch()) //
.withNumberOfNodes(clusterHealthResponse.getNumberOfNodes()) //
.withNumberOfPendingTasks(clusterHealthResponse.getNumberOfPendingTasks()) //
.withRelocatingShards(clusterHealthResponse.getRelocatingShards()) //
.withStatus(clusterHealthResponse.getStatus().toString()) //
.withTaskMaxWaitingTimeMillis(clusterHealthResponse.getTaskMaxWaitingTime().millis()) //
.withTimedOut(clusterHealthResponse.isTimedOut()) //
.withUnassignedShards(clusterHealthResponse.getUnassignedShards()) //
.build(); //
}
// endregion
// region byQueryResponse
public static ByQueryResponse byQueryResponseOf(BulkByScrollResponse bulkByScrollResponse) {
final List<ByQueryResponse.Failure> failures = bulkByScrollResponse.getBulkFailures() //
.stream() //
.map(ResponseConverter::byQueryResponseFailureOf) //
.collect(Collectors.toList()); //
final List<ByQueryResponse.SearchFailure> searchFailures = bulkByScrollResponse.getSearchFailures() //
.stream() //
.map(ResponseConverter::byQueryResponseSearchFailureOf) //
.collect(Collectors.toList());//
return ByQueryResponse.builder() //
.withTook(bulkByScrollResponse.getTook().getMillis()) //
.withTimedOut(bulkByScrollResponse.isTimedOut()) //
.withTotal(bulkByScrollResponse.getTotal()) //
.withUpdated(bulkByScrollResponse.getUpdated()) //
.withDeleted(bulkByScrollResponse.getDeleted()) //
.withBatches(bulkByScrollResponse.getBatches()) //
.withVersionConflicts(bulkByScrollResponse.getVersionConflicts()) //
.withNoops(bulkByScrollResponse.getNoops()) //
.withBulkRetries(bulkByScrollResponse.getBulkRetries()) //
.withSearchRetries(bulkByScrollResponse.getSearchRetries()) //
.withReasonCancelled(bulkByScrollResponse.getReasonCancelled()) //
.withFailures(failures) //
.withSearchFailure(searchFailures) //
.build(); //
}
/**
* Create a new {@link ByQueryResponse.Failure} from {@link BulkItemResponse.Failure}
*
* @param failure {@link BulkItemResponse.Failure} to translate
* @return a new {@link ByQueryResponse.Failure}
*/
public static ByQueryResponse.Failure byQueryResponseFailureOf(BulkItemResponse.Failure failure) {
return ByQueryResponse.Failure.builder() //
.withIndex(failure.getIndex()) //
.withType(failure.getType()) //
.withId(failure.getId()) //
.withStatus(failure.getStatus().getStatus()) //
.withAborted(failure.isAborted()) //
.withCause(failure.getCause()) //
.withSeqNo(failure.getSeqNo()) //
.withTerm(failure.getTerm()) //
.build(); //
}
/**
* Create a new {@link ByQueryResponse.SearchFailure} from {@link ScrollableHitSource.SearchFailure}
*
* @param searchFailure {@link ScrollableHitSource.SearchFailure} to translate
* @return a new {@link ByQueryResponse.SearchFailure}
*/
public static ByQueryResponse.SearchFailure byQueryResponseSearchFailureOf(
ScrollableHitSource.SearchFailure searchFailure) {
return ByQueryResponse.SearchFailure.builder() //
.withReason(searchFailure.getReason()) //
.withIndex(searchFailure.getIndex()) //
.withNodeId(searchFailure.getNodeId()) //
.withShardId(searchFailure.getShardId()) //
.withStatus(searchFailure.getStatus().getStatus()) //
.build(); //
}
// endregion
// region reindex
/**
* @since 4.4
*/
public static ReindexResponse reindexResponseOf(BulkByScrollResponse bulkByScrollResponse) {
final List<ReindexResponse.Failure> failures = bulkByScrollResponse.getBulkFailures() //
.stream() //
.map(ResponseConverter::reindexResponseFailureOf) //
.collect(Collectors.toList()); //
return ReindexResponse.builder() //
.withTook(bulkByScrollResponse.getTook().getMillis()) //
.withTimedOut(bulkByScrollResponse.isTimedOut()) //
.withTotal(bulkByScrollResponse.getTotal()) //
.withCreated(bulkByScrollResponse.getCreated()) //
.withUpdated(bulkByScrollResponse.getUpdated()) //
.withDeleted(bulkByScrollResponse.getDeleted()) //
.withBatches(bulkByScrollResponse.getBatches()) //
.withVersionConflicts(bulkByScrollResponse.getVersionConflicts()) //
.withNoops(bulkByScrollResponse.getNoops()) //
.withBulkRetries(bulkByScrollResponse.getBulkRetries()) //
.withSearchRetries(bulkByScrollResponse.getSearchRetries()) //
.withThrottledMillis(bulkByScrollResponse.getStatus().getThrottled().getMillis()) //
.withRequestsPerSecond(bulkByScrollResponse.getStatus().getRequestsPerSecond()) //
.withThrottledUntilMillis(bulkByScrollResponse.getStatus().getThrottledUntil().getMillis()) //
.withFailures(failures) //
.build(); //
}
/**
* @since 4.4
*/
public static ReindexResponse.Failure reindexResponseFailureOf(BulkItemResponse.Failure failure) {
return ReindexResponse.Failure.builder() //
.withIndex(failure.getIndex()) //
.withType(failure.getType()) //
.withId(failure.getId()) //
.withStatus(failure.getStatus().getStatus()) //
.withAborted(failure.isAborted()) //
.withCause(failure.getCause()) //
.withSeqNo(failure.getSeqNo()) //
.withTerm(failure.getTerm()) //
.build(); //
}
// endregion
}

View File

@ -1,270 +0,0 @@
/*
* Copyright 2018-2023 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.erhlc;
import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.time.Duration;
import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.HttpRequestInterceptor;
import org.apache.http.HttpResponse;
import org.apache.http.HttpResponseInterceptor;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.config.RequestConfig.Builder;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.impl.nio.client.HttpAsyncClientBuilder;
import org.apache.http.message.BasicHeader;
import org.apache.http.protocol.HttpContext;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.RestHighLevelClientBuilder;
import org.springframework.data.elasticsearch.client.ClientConfiguration;
import org.springframework.data.elasticsearch.client.ClientLogger;
import org.springframework.data.elasticsearch.support.HttpHeaders;
import org.springframework.util.Assert;
/**
* Utility class for common access to Elasticsearch clients. {@link RestClients} consolidates set up routines for the
* various drivers into a single place.
*
* @author Christoph Strobl
* @author Mark Paluch
* @author Huw Ayling-Miller
* @author Henrique Amaral
* @author Peter-Josef Meisch
* @author Nic Hines
* @since 3.2
* @deprecated since 5.0
*/
@Deprecated
public final class RestClients {
/**
* Name of whose value can be used to correlate log messages for this request.
*/
private static final String LOG_ID_ATTRIBUTE = RestClients.class.getName() + ".LOG_ID";
private RestClients() {}
/**
* Start here to create a new client tailored to your needs.
*
* @return new instance of {@link ElasticsearchRestClient}.
*/
public static ElasticsearchRestClient create(ClientConfiguration clientConfiguration) {
Assert.notNull(clientConfiguration, "ClientConfiguration must not be null!");
HttpHost[] httpHosts = formattedHosts(clientConfiguration.getEndpoints(), clientConfiguration.useSsl()).stream()
.map(HttpHost::create).toArray(HttpHost[]::new);
RestClientBuilder builder = RestClient.builder(httpHosts);
if (clientConfiguration.getPathPrefix() != null) {
builder.setPathPrefix(clientConfiguration.getPathPrefix());
}
HttpHeaders headers = clientConfiguration.getDefaultHeaders();
if (!headers.isEmpty()) {
builder.setDefaultHeaders(toHeaderArray(headers));
}
builder.setHttpClientConfigCallback(clientBuilder -> {
clientConfiguration.getSslContext().ifPresent(clientBuilder::setSSLContext);
clientConfiguration.getHostNameVerifier().ifPresent(clientBuilder::setSSLHostnameVerifier);
clientBuilder.addInterceptorLast(new CustomHeaderInjector(clientConfiguration.getHeadersSupplier()));
if (ClientLogger.isEnabled()) {
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
clientBuilder.addInterceptorLast((HttpRequestInterceptor) interceptor);
clientBuilder.addInterceptorLast((HttpResponseInterceptor) interceptor);
}
Builder requestConfigBuilder = RequestConfig.custom();
Duration connectTimeout = clientConfiguration.getConnectTimeout();
if (!connectTimeout.isNegative()) {
requestConfigBuilder.setConnectTimeout(Math.toIntExact(connectTimeout.toMillis()));
}
Duration socketTimeout = clientConfiguration.getSocketTimeout();
if (!socketTimeout.isNegative()) {
requestConfigBuilder.setSocketTimeout(Math.toIntExact(socketTimeout.toMillis()));
requestConfigBuilder.setConnectionRequestTimeout(Math.toIntExact(socketTimeout.toMillis()));
}
clientBuilder.setDefaultRequestConfig(requestConfigBuilder.build());
clientConfiguration.getProxy().map(HttpHost::create).ifPresent(clientBuilder::setProxy);
for (ClientConfiguration.ClientConfigurationCallback<?> clientConfigurer : clientConfiguration
.getClientConfigurers()) {
if (clientConfigurer instanceof RestClientConfigurationCallback restClientConfigurationCallback) {
clientBuilder = restClientConfigurationCallback.configure(clientBuilder);
}
}
return clientBuilder;
});
RestHighLevelClient client = new RestHighLevelClientBuilder(builder.build()).setApiCompatibilityMode(true).build();
return () -> client;
}
private static Header[] toHeaderArray(HttpHeaders headers) {
return headers.entrySet().stream() //
.flatMap(entry -> entry.getValue().stream() //
.map(value -> new BasicHeader(entry.getKey(), value))) //
.toArray(Header[]::new);
}
private static List<String> formattedHosts(List<InetSocketAddress> hosts, boolean useSsl) {
return hosts.stream().map(it -> (useSsl ? "https" : "http") + "://" + it.getHostString() + ":" + it.getPort())
.collect(Collectors.toList());
}
/**
* @author Christoph Strobl
*/
@FunctionalInterface
public interface ElasticsearchRestClient extends Closeable {
/**
* Apply the configuration to create a {@link RestHighLevelClient}.
*
* @return new instance of {@link RestHighLevelClient}.
*/
RestHighLevelClient rest();
/**
* Apply the configuration to create a {@link RestClient}.
*
* @return new instance of {@link RestClient}.
*/
default RestClient lowLevelRest() {
return rest().getLowLevelClient();
}
@Override
default void close() throws IOException {
rest().close();
}
}
/**
* Logging interceptors for Elasticsearch client logging.
*
* @see ClientLogger
* @since 3.2
* @deprecated since 5.0
*/
@Deprecated
private static class HttpLoggingInterceptor implements HttpResponseInterceptor, HttpRequestInterceptor {
@Override
public void process(HttpRequest request, HttpContext context) throws IOException {
String logId = (String) context.getAttribute(RestClients.LOG_ID_ATTRIBUTE);
if (logId == null) {
logId = ClientLogger.newLogId();
context.setAttribute(RestClients.LOG_ID_ATTRIBUTE, logId);
}
if (request instanceof HttpEntityEnclosingRequest entityRequest
&& ((HttpEntityEnclosingRequest) request).getEntity() != null) {
HttpEntity entity = ((HttpEntityEnclosingRequest) request).getEntity();
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
entity.writeTo(buffer);
if (!entity.isRepeatable()) {
entityRequest.setEntity(new ByteArrayEntity(buffer.toByteArray()));
}
ClientLogger.logRequest(logId, request.getRequestLine().getMethod(), request.getRequestLine().getUri(), "",
buffer::toString);
} else {
ClientLogger.logRequest(logId, request.getRequestLine().getMethod(), request.getRequestLine().getUri(), "");
}
}
@Override
public void process(HttpResponse response, HttpContext context) {
String logId = (String) context.getAttribute(RestClients.LOG_ID_ATTRIBUTE);
ClientLogger.logRawResponse(logId, response.getStatusLine().getStatusCode());
}
}
/**
* Interceptor to inject custom supplied headers.
*
* @since 4.0
*/
private static class CustomHeaderInjector implements HttpRequestInterceptor {
public CustomHeaderInjector(Supplier<HttpHeaders> headersSupplier) {
this.headersSupplier = headersSupplier;
}
private final Supplier<HttpHeaders> headersSupplier;
@Override
public void process(HttpRequest request, HttpContext context) {
HttpHeaders httpHeaders = headersSupplier.get();
if (httpHeaders != null && !httpHeaders.isEmpty()) {
Arrays.stream(toHeaderArray(httpHeaders)).forEach(request::addHeader);
}
}
}
/**
* {@link org.springframework.data.elasticsearch.client.ClientConfiguration.ClientConfigurationCallback} to configure
* the RestClient with a {@link HttpAsyncClientBuilder}
*
* @since 4.3
* @deprecated since 5.0
*/
@Deprecated
public interface RestClientConfigurationCallback
extends ClientConfiguration.ClientConfigurationCallback<HttpAsyncClientBuilder> {
static RestClientConfigurationCallback from(
Function<HttpAsyncClientBuilder, HttpAsyncClientBuilder> clientBuilderCallback) {
Assert.notNull(clientBuilderCallback, "clientBuilderCallback must not be null");
// noinspection NullableProblems
return clientBuilderCallback::apply;
}
}
}

View File

@ -1,48 +0,0 @@
/*
* Copyright 2018-2023 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.erhlc;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.w3c.dom.Element;
/**
* @author Don Wellington
* @deprecated since 5.0
*/
@Deprecated
public class RestHighLevelClientBeanDefinitionParser extends AbstractBeanDefinitionParser {
@Override
protected AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(RestHighLevelClientFactoryBean.class);
setConfigurations(element, builder);
return getSourcedBeanDefinition(builder, element, parserContext);
}
private void setConfigurations(Element element, BeanDefinitionBuilder builder) {
builder.addPropertyValue("hosts", element.getAttribute("hosts"));
}
private AbstractBeanDefinition getSourcedBeanDefinition(BeanDefinitionBuilder builder, Element source,
ParserContext context) {
AbstractBeanDefinition definition = builder.getBeanDefinition();
definition.setSource(context.extractSource(source));
return definition;
}
}

View File

@ -1,106 +0,0 @@
/*
* Copyright 2018-2023 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.erhlc;
import java.net.URL;
import java.util.ArrayList;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.FactoryBeanNotInitializedException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
* RestHighLevelClientFactoryBean
*
* @author Don Wellington
* @author Peter-Josef Meisch
* @deprecated since 5.0
*/
@Deprecated
public class RestHighLevelClientFactoryBean
implements FactoryBean<RestHighLevelClient>, InitializingBean, DisposableBean {
private static final Log LOGGER = LogFactory.getLog(RestHighLevelClientFactoryBean.class);
private @Nullable RestHighLevelClient client;
private String hosts = "http://localhost:9200";
static final String COMMA = ",";
@Override
public void destroy() {
try {
LOGGER.info("Closing elasticSearch client");
if (client != null) {
client.close();
}
} catch (final Exception e) {
LOGGER.error("Error closing ElasticSearch client: ", e);
}
}
@Override
public void afterPropertiesSet() throws Exception {
buildClient();
}
@Override
public RestHighLevelClient getObject() {
if (client == null) {
throw new FactoryBeanNotInitializedException();
}
return client;
}
@Override
public Class<?> getObjectType() {
return RestHighLevelClient.class;
}
@Override
public boolean isSingleton() {
return false;
}
protected void buildClient() throws Exception {
Assert.hasText(hosts, "[Assertion Failed] At least one host must be set.");
ArrayList<HttpHost> httpHosts = new ArrayList<>();
for (String host : hosts.split(COMMA)) {
URL hostUrl = new URL(host);
httpHosts.add(new HttpHost(hostUrl.getHost(), hostUrl.getPort(), hostUrl.getProtocol()));
}
client = new RestHighLevelClient(RestClient.builder(httpHosts.toArray(new HttpHost[httpHosts.size()])));
}
public void setHosts(String hosts) {
this.hosts = hosts;
}
public String getHosts() {
return this.hosts;
}
}

View File

@ -1,281 +0,0 @@
/*
* Copyright 2019-2023 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.erhlc;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
import org.elasticsearch.action.admin.indices.alias.get.GetAliasesRequest;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.admin.indices.refresh.RefreshRequest;
import org.elasticsearch.action.admin.indices.settings.get.GetSettingsRequest;
import org.elasticsearch.action.admin.indices.settings.get.GetSettingsResponse;
import org.elasticsearch.action.admin.indices.template.delete.DeleteIndexTemplateRequest;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.client.indices.GetIndexResponse;
import org.elasticsearch.client.indices.GetIndexTemplatesRequest;
import org.elasticsearch.client.indices.GetIndexTemplatesResponse;
import org.elasticsearch.client.indices.GetMappingsRequest;
import org.elasticsearch.client.indices.IndexTemplatesExistRequest;
import org.elasticsearch.client.indices.PutIndexTemplateRequest;
import org.elasticsearch.client.indices.PutMappingRequest;
import org.elasticsearch.cluster.metadata.MappingMetadata;
import org.springframework.data.elasticsearch.core.AbstractIndexTemplate;
import org.springframework.data.elasticsearch.core.IndexInformation;
import org.springframework.data.elasticsearch.core.IndexOperations;
import org.springframework.data.elasticsearch.core.document.Document;
import org.springframework.data.elasticsearch.core.index.*;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
* {@link IndexOperations} implementation using the RestClient.
*
* @author Peter-Josef Meisch
* @author Sascha Woo
* @author George Popides
* @since 4.0
* @deprecated since 5.0
*/
@Deprecated
class RestIndexTemplate extends AbstractIndexTemplate implements IndexOperations {
private static final Log LOGGER = LogFactory.getLog(RestIndexTemplate.class);
private final ElasticsearchRestTemplate restTemplate;
protected final RequestFactory requestFactory;
public RestIndexTemplate(ElasticsearchRestTemplate restTemplate, Class<?> boundClass) {
super(restTemplate.getElasticsearchConverter(), boundClass);
this.restTemplate = restTemplate;
requestFactory = new RequestFactory(elasticsearchConverter);
}
public RestIndexTemplate(ElasticsearchRestTemplate restTemplate, IndexCoordinates boundIndex) {
super(restTemplate.getElasticsearchConverter(), boundIndex);
this.restTemplate = restTemplate;
requestFactory = new RequestFactory(elasticsearchConverter);
}
@Override
protected boolean doCreate(IndexCoordinates index, Map<String, Object> settings, @Nullable Document mapping) {
CreateIndexRequest request = requestFactory.createIndexRequest(index, settings, mapping);
return restTemplate.execute(client -> client.indices().create(request, RequestOptions.DEFAULT).isAcknowledged());
}
@Override
protected boolean doDelete(IndexCoordinates index) {
Assert.notNull(index, "index must not be null");
if (doExists(index)) {
DeleteIndexRequest deleteIndexRequest = requestFactory.deleteIndexRequest(index);
return restTemplate
.execute(client -> client.indices().delete(deleteIndexRequest, RequestOptions.DEFAULT).isAcknowledged());
}
return false;
}
@Override
protected boolean doExists(IndexCoordinates index) {
GetIndexRequest getIndexRequest = requestFactory.getIndexRequest(index);
return restTemplate.execute(client -> client.indices().exists(getIndexRequest, RequestOptions.DEFAULT));
}
@Override
protected boolean doPutMapping(IndexCoordinates index, Document mapping) {
Assert.notNull(index, "No index defined for putMapping()");
PutMappingRequest request = requestFactory.putMappingRequest(index, mapping);
return restTemplate
.execute(client -> client.indices().putMapping(request, RequestOptions.DEFAULT).isAcknowledged());
}
@Override
protected Map<String, Object> doGetMapping(IndexCoordinates index) {
Assert.notNull(index, "No index defined for doGetMapping()");
GetMappingsRequest mappingsRequest = requestFactory.getMappingsRequest(index);
return restTemplate.execute(client -> {
Map<String, MappingMetadata> mappings = client.indices() //
.getMapping(mappingsRequest, RequestOptions.DEFAULT) //
.mappings(); //
if (mappings == null || mappings.size() == 0) {
return Collections.emptyMap();
}
if (mappings.size() > 1) {
LOGGER.warn("more than one mapping returned for " + index.getIndexName());
}
// we have at least one, take the first from the iterator
return mappings.entrySet().iterator().next().getValue().getSourceAsMap();
});
}
@Override
protected Map<String, Set<AliasData>> doGetAliases(@Nullable String[] aliasNames, @Nullable String[] indexNames) {
GetAliasesRequest getAliasesRequest = requestFactory.getAliasesRequest(aliasNames, indexNames);
return restTemplate.execute(client -> ResponseConverter
.aliasDatas(client.indices().getAlias(getAliasesRequest, RequestOptions.DEFAULT).getAliases()));
}
@Override
public boolean alias(AliasActions aliasActions) {
IndicesAliasesRequest request = requestFactory.indicesAliasesRequest(aliasActions);
return restTemplate
.execute(client -> client.indices().updateAliases(request, RequestOptions.DEFAULT).isAcknowledged());
}
@Override
protected Settings doGetSettings(IndexCoordinates index, boolean includeDefaults) {
Assert.notNull(index, "index must not be null");
GetSettingsRequest getSettingsRequest = requestFactory.getSettingsRequest(index, includeDefaults);
GetSettingsResponse response = restTemplate.execute(client -> client.indices() //
.getSettings(getSettingsRequest, RequestOptions.DEFAULT));
return ResponseConverter.fromSettingsResponse(response, getSettingsRequest.indices()[0]);
}
@Override
protected void doRefresh(IndexCoordinates index) {
Assert.notNull(index, "No index defined for refresh()");
RefreshRequest refreshRequest = requestFactory.refreshRequest(index);
restTemplate.execute(client -> client.indices().refresh(refreshRequest, RequestOptions.DEFAULT));
}
@Override
public boolean putTemplate(PutTemplateRequest putTemplateRequest) {
Assert.notNull(putTemplateRequest, "putTemplateRequest must not be null");
PutIndexTemplateRequest putIndexTemplateRequest = requestFactory.putIndexTemplateRequest(putTemplateRequest);
return restTemplate.execute(
client -> client.indices().putTemplate(putIndexTemplateRequest, RequestOptions.DEFAULT).isAcknowledged());
}
@Override
public TemplateData getTemplate(GetTemplateRequest getTemplateRequest) {
Assert.notNull(getTemplateRequest, "getTemplateRequest must not be null");
// getIndexTemplate throws an error on non-existing template names
if (!existsTemplate(new ExistsTemplateRequest(getTemplateRequest.getTemplateName()))) {
return null;
}
GetIndexTemplatesRequest getIndexTemplatesRequest = requestFactory.getIndexTemplatesRequest(getTemplateRequest);
GetIndexTemplatesResponse getIndexTemplatesResponse = restTemplate
.execute(client -> client.indices().getIndexTemplate(getIndexTemplatesRequest, RequestOptions.DEFAULT));
return ResponseConverter.getTemplateData(getIndexTemplatesResponse, getTemplateRequest.getTemplateName());
}
@Override
public boolean existsTemplate(ExistsTemplateRequest existsTemplateRequest) {
Assert.notNull(existsTemplateRequest, "existsTemplateRequest must not be null");
IndexTemplatesExistRequest putIndexTemplateRequest = requestFactory
.indexTemplatesExistsRequest(existsTemplateRequest);
return restTemplate
.execute(client -> client.indices().existsTemplate(putIndexTemplateRequest, RequestOptions.DEFAULT));
}
@Override
public boolean deleteTemplate(DeleteTemplateRequest deleteTemplateRequest) {
Assert.notNull(deleteTemplateRequest, "deleteTemplateRequest must not be null");
DeleteIndexTemplateRequest deleteIndexTemplateRequest = requestFactory
.deleteIndexTemplateRequest(deleteTemplateRequest);
return restTemplate.execute(
client -> client.indices().deleteTemplate(deleteIndexTemplateRequest, RequestOptions.DEFAULT).isAcknowledged());
}
@Override
public boolean putIndexTemplate(
org.springframework.data.elasticsearch.core.index.PutIndexTemplateRequest putIndexTemplateRequest) {
throw new UnsupportedOperationException("not implemented");
}
@Override
public boolean existsIndexTemplate(ExistsIndexTemplateRequest existsTemplateRequest) {
throw new UnsupportedOperationException("not implemented");
}
@Override
public List<TemplateResponse> getIndexTemplate(GetIndexTemplateRequest getIndexTemplateRequest) {
throw new UnsupportedOperationException("not implemented");
}
@Override
public boolean deleteIndexTemplate(
org.springframework.data.elasticsearch.core.index.DeleteIndexTemplateRequest deleteIndexTemplateRequest) {
throw new UnsupportedOperationException("not implemented");
}
@Override
public boolean putComponentTemplate(PutComponentTemplateRequest putComponentTemplateRequest) {
throw new UnsupportedOperationException("not implemented");
}
@Override
public boolean existsComponentTemplate(ExistsComponentTemplateRequest existsComponentTemplateRequest) {
throw new UnsupportedOperationException("not implemented");
}
@Override
public List<TemplateResponse> getComponentTemplate(GetComponentTemplateRequest getComponentTemplateRequest) {
throw new UnsupportedOperationException("not implemented");
}
@Override
public boolean deleteComponentTemplate(DeleteComponentTemplateRequest deleteComponentTemplateRequest) {
throw new UnsupportedOperationException("not implemented");
}
@Override
public List<IndexInformation> getInformation(IndexCoordinates index) {
Assert.notNull(index, "index must not be null");
GetIndexRequest request = requestFactory.getIndexRequest(index);
return restTemplate.execute(client -> {
GetIndexResponse getIndexResponse = client.indices().get(request, RequestOptions.DEFAULT);
return ResponseConverter.getIndexInformations(getIndexResponse);
});
}
// endregion
}

View File

@ -1,43 +0,0 @@
/*
* Copyright 2021-2023 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.erhlc;
import org.elasticsearch.script.Script;
/**
* @author Ryan Murfitt
* @author Artur Konczak
* @deprecated since 5.0
*/
@Deprecated
public class ScriptField {
private final String fieldName;
private final Script script;
public ScriptField(String fieldName, Script script) {
this.fieldName = fieldName;
this.script = script;
}
public String fieldName() {
return fieldName;
}
public Script script() {
return script;
}
}

View File

@ -1,225 +0,0 @@
/*
* Copyright 2022-2023 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.erhlc;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.lucene.search.TotalHits;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.Aggregations;
import org.springframework.data.elasticsearch.core.document.SearchDocument;
import org.springframework.data.elasticsearch.core.document.SearchDocumentResponse;
import org.springframework.data.elasticsearch.core.suggest.response.CompletionSuggestion;
import org.springframework.data.elasticsearch.core.suggest.response.PhraseSuggestion;
import org.springframework.data.elasticsearch.core.suggest.response.SortBy;
import org.springframework.data.elasticsearch.core.suggest.response.Suggest;
import org.springframework.data.elasticsearch.core.suggest.response.TermSuggestion;
import org.springframework.data.elasticsearch.support.ScoreDoc;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
* Factory class to create {@link SearchDocumentResponse} instances.
*
* @author Peter-Josef Meisch
* @since 4.0
* @deprecated since 5.0
*/
@Deprecated
public class SearchDocumentResponseBuilder {
private static final Log LOGGER = LogFactory.getLog(SearchDocumentResponse.class);
/**
* creates a SearchDocumentResponse from the {@link SearchResponse}
*
* @param searchResponse must not be {@literal null}
* @param entityCreator function to create an entity from a {@link SearchDocument}
* @param <T> entity type
* @return the SearchDocumentResponse
*/
public static <T> SearchDocumentResponse from(SearchResponse searchResponse,
SearchDocumentResponse.EntityCreator<T> entityCreator) {
Assert.notNull(searchResponse, "searchResponse must not be null");
SearchHits searchHits = searchResponse.getHits();
String scrollId = searchResponse.getScrollId();
Aggregations aggregations = searchResponse.getAggregations();
org.elasticsearch.search.suggest.Suggest suggest = searchResponse.getSuggest();
return from(searchHits, scrollId, aggregations, suggest, entityCreator);
}
/**
* creates a {@link SearchDocumentResponseBuilder} from {@link SearchHits} with the given scrollId aggregations and
* suggest
*
* @param searchHits the {@link SearchHits} to process
* @param scrollId scrollId
* @param aggregations aggregations
* @param suggestES the suggestion response from Elasticsearch
* @param entityCreator function to create an entity from a {@link SearchDocument}
* @param <T> entity type
* @return the {@link SearchDocumentResponse}
* @since 4.3
*/
public static <T> SearchDocumentResponse from(SearchHits searchHits, @Nullable String scrollId,
@Nullable Aggregations aggregations, @Nullable org.elasticsearch.search.suggest.Suggest suggestES,
SearchDocumentResponse.EntityCreator<T> entityCreator) {
TotalHits responseTotalHits = searchHits.getTotalHits();
long totalHits;
String totalHitsRelation;
if (responseTotalHits != null) {
totalHits = responseTotalHits.value;
totalHitsRelation = responseTotalHits.relation.name();
} else {
totalHits = searchHits.getHits().length;
totalHitsRelation = "OFF";
}
float maxScore = searchHits.getMaxScore();
List<SearchDocument> searchDocuments = new ArrayList<>();
for (SearchHit searchHit : searchHits) {
if (searchHit != null) {
searchDocuments.add(DocumentAdapters.from(searchHit));
}
}
ElasticsearchAggregations aggregationsContainer = aggregations != null ? new ElasticsearchAggregations(aggregations)
: null;
Suggest suggest = suggestFrom(suggestES, entityCreator);
// no pointInTimeId for the deprecated implementation
return new SearchDocumentResponse(totalHits, totalHitsRelation, maxScore, scrollId, null, searchDocuments,
aggregationsContainer, suggest);
}
@Nullable
private static <T> Suggest suggestFrom(@Nullable org.elasticsearch.search.suggest.Suggest suggestES,
SearchDocumentResponse.EntityCreator<T> entityCreator) {
if (suggestES == null) {
return null;
}
List<Suggest.Suggestion<? extends Suggest.Suggestion.Entry<? extends Suggest.Suggestion.Entry.Option>>> suggestions = new ArrayList<>();
for (org.elasticsearch.search.suggest.Suggest.Suggestion<? extends org.elasticsearch.search.suggest.Suggest.Suggestion.Entry<? extends org.elasticsearch.search.suggest.Suggest.Suggestion.Entry.Option>> suggestionES : suggestES) {
if (suggestionES instanceof org.elasticsearch.search.suggest.term.TermSuggestion termSuggestionES) {
List<TermSuggestion.Entry> entries = new ArrayList<>();
for (org.elasticsearch.search.suggest.term.TermSuggestion.Entry entryES : termSuggestionES) {
List<TermSuggestion.Entry.Option> options = new ArrayList<>();
for (org.elasticsearch.search.suggest.term.TermSuggestion.Entry.Option optionES : entryES) {
options.add(new TermSuggestion.Entry.Option(textToString(optionES.getText()),
textToString(optionES.getHighlighted()), optionES.getScore(), optionES.collateMatch(),
optionES.getFreq()));
}
entries.add(new TermSuggestion.Entry(textToString(entryES.getText()), entryES.getOffset(),
entryES.getLength(), options));
}
suggestions.add(new TermSuggestion(termSuggestionES.getName(), termSuggestionES.getSize(), entries,
suggestFrom(termSuggestionES.getSort())));
}
if (suggestionES instanceof org.elasticsearch.search.suggest.phrase.PhraseSuggestion phraseSuggestionES) {
List<PhraseSuggestion.Entry> entries = new ArrayList<>();
for (org.elasticsearch.search.suggest.phrase.PhraseSuggestion.Entry entryES : phraseSuggestionES) {
List<PhraseSuggestion.Entry.Option> options = new ArrayList<>();
for (org.elasticsearch.search.suggest.phrase.PhraseSuggestion.Entry.Option optionES : entryES) {
options.add(new PhraseSuggestion.Entry.Option(textToString(optionES.getText()),
textToString(optionES.getHighlighted()), (double) optionES.getScore(), optionES.collateMatch()));
}
entries.add(new PhraseSuggestion.Entry(textToString(entryES.getText()), entryES.getOffset(),
entryES.getLength(), options, entryES.getCutoffScore()));
}
suggestions.add(new PhraseSuggestion(phraseSuggestionES.getName(), phraseSuggestionES.getSize(), entries));
}
if (suggestionES instanceof org.elasticsearch.search.suggest.completion.CompletionSuggestion completionSuggestionES) {
List<CompletionSuggestion.Entry<T>> entries = new ArrayList<>();
for (org.elasticsearch.search.suggest.completion.CompletionSuggestion.Entry entryES : completionSuggestionES) {
List<CompletionSuggestion.Entry.Option<T>> options = new ArrayList<>();
for (org.elasticsearch.search.suggest.completion.CompletionSuggestion.Entry.Option optionES : entryES) {
SearchDocument searchDocument = optionES.getHit() != null ? DocumentAdapters.from(optionES.getHit()) : null;
T hitEntity = null;
if (searchDocument != null) {
try {
hitEntity = entityCreator.apply(searchDocument).get();
} catch (Exception e) {
if (LOGGER.isWarnEnabled()) {
LOGGER.warn("Error creating entity from SearchDocument");
}
}
}
options.add(new CompletionSuggestion.Entry.Option<>(textToString(optionES.getText()),
textToString(optionES.getHighlighted()), (double) optionES.getScore(), optionES.collateMatch(),
optionES.getContexts(), scoreDocFrom(optionES.getDoc()), searchDocument, hitEntity));
}
entries.add(new CompletionSuggestion.Entry<>(textToString(entryES.getText()), entryES.getOffset(),
entryES.getLength(), options));
}
suggestions.add(
new CompletionSuggestion<>(completionSuggestionES.getName(), completionSuggestionES.getSize(), entries));
}
}
return new Suggest(suggestions, suggestES.hasScoreDocs());
}
private static SortBy suggestFrom(org.elasticsearch.search.suggest.SortBy sort) {
return SortBy.valueOf(sort.name().toUpperCase());
}
@Nullable
private static ScoreDoc scoreDocFrom(@Nullable org.apache.lucene.search.ScoreDoc scoreDoc) {
if (scoreDoc == null) {
return null;
}
return new ScoreDoc(scoreDoc.score, scoreDoc.doc, scoreDoc.shardIndex);
}
private static String textToString(@Nullable Text text) {
return text != null ? text.string() : "";
}
}

View File

@ -1,34 +0,0 @@
/*
* Copyright 2019-2023 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.erhlc;
import org.elasticsearch.search.SearchHits;
/**
* Utility class to prevent leaking of Lucene API into Spring Data Elasticsearch.
*
* @author Peter-Josef Meisch
* @since 4.4
* @deprecated since 5.0
*/
@Deprecated
public final class SearchHitsUtil {
private SearchHitsUtil() {}
public static long getTotalCount(SearchHits searchHits) {
return searchHits.getTotalHits().value;
}
}

View File

@ -1,110 +0,0 @@
/*
* Copyright 2018-2023 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.erhlc;
import reactor.core.publisher.Mono;
import java.net.InetSocketAddress;
import java.util.Collections;
import org.springframework.data.elasticsearch.client.ElasticsearchHost;
import org.springframework.data.elasticsearch.client.ElasticsearchHost.State;
import org.springframework.data.elasticsearch.client.NoReachableHostException;
import org.springframework.web.reactive.function.client.WebClient;
/**
* {@link HostProvider} for a single host.
*
* @author Christoph Strobl
* @author Mark Paluch
* @author Peter-Josef Meisch
* @since 3.2
* @deprecated since 5.0
*/
@Deprecated
class SingleNodeHostProvider implements HostProvider<SingleNodeHostProvider> {
private final WebClientProvider clientProvider;
private final InetSocketAddress endpoint;
private volatile ElasticsearchHost state;
SingleNodeHostProvider(WebClientProvider clientProvider, InetSocketAddress endpoint) {
this.clientProvider = clientProvider;
this.endpoint = endpoint;
this.state = new ElasticsearchHost(this.endpoint, State.UNKNOWN);
}
/*
* (non-Javadoc)
* @see org.springframework.data.elasticsearch.client.erhlc.HostProvider#clusterInfo()
*/
@Override
public Mono<ClusterInformation> clusterInfo() {
return createWebClient(endpoint) //
.head().uri("/") //
.exchangeToMono(it -> {
if (it.statusCode().isError()) {
state = ElasticsearchHost.offline(endpoint);
} else {
state = ElasticsearchHost.online(endpoint);
}
return Mono.just(state);
}).onErrorResume(throwable -> {
state = ElasticsearchHost.offline(endpoint);
clientProvider.getErrorListener().accept(throwable);
return Mono.just(state);
}).map(elasticsearchHost -> new ClusterInformation(Collections.singleton(elasticsearchHost)));
}
/*
* (non-Javadoc)
* @see org.springframework.data.elasticsearch.client.erhlc.HostProvider#createWebClient(java.net.InetSocketAddress)
*/
@Override
public WebClient createWebClient(InetSocketAddress endpoint) {
return this.clientProvider.get(endpoint);
}
/*
* (non-Javadoc)
* @see org.springframework.data.elasticsearch.client.erhlc.HostProvider#lookupActiveHost(org.springframework.data.elasticsearch.client.erhlc.HostProvider.Verification)
*/
@Override
public Mono<InetSocketAddress> lookupActiveHost(Verification verification) {
if (Verification.LAZY.equals(verification) && state.isOnline()) {
return Mono.just(endpoint);
}
return clusterInfo().handle((information, sink) -> {
ElasticsearchHost host = information.getNodes().iterator().next();
if (host.isOnline()) {
sink.next(host.getEndpoint());
return;
}
sink.error(new NoReachableHostException(Collections.singleton(host)));
});
}
ElasticsearchHost getCachedHostState() {
return state;
}
}

View File

@ -1,270 +0,0 @@
/*
* Copyright 2018-2023 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.erhlc;
import io.netty.channel.ChannelOption;
import io.netty.handler.ssl.ApplicationProtocolConfig;
import io.netty.handler.ssl.ClientAuth;
import io.netty.handler.ssl.IdentityCipherSuiteFilter;
import io.netty.handler.ssl.JdkSslContext;
import io.netty.handler.timeout.ReadTimeoutHandler;
import io.netty.handler.timeout.WriteTimeoutHandler;
import reactor.netty.http.client.HttpClient;
import reactor.netty.transport.ProxyProvider;
import java.net.InetSocketAddress;
import java.time.Duration;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import java.util.function.Function;
import javax.net.ssl.SSLContext;
import org.springframework.data.elasticsearch.client.ClientConfiguration;
import org.springframework.http.HttpHeaders;
import org.springframework.http.client.reactive.ClientHttpConnector;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.web.reactive.function.client.WebClient;
/**
* Provider for {@link WebClient}s using a pre-configured {@code scheme}. This class returns {@link WebClient} for a
* specific {@link InetSocketAddress endpoint} and encapsulates common configuration aspects of {@link WebClient} so
* that code using {@link WebClient} is not required to apply further configuration to the actual client. <br/>
* Client instances are typically cached allowing reuse of pooled connections if configured on the
* {@link ClientHttpConnector}.
*
* @author Christoph Strobl
* @author Mark Paluch
* @author Huw Ayling-Miller
* @author Peter-Josef Meisch
* @since 3.2
* @deprecated since 5.0
*/
@Deprecated
public interface WebClientProvider {
/**
* Creates a new {@link WebClientProvider} using the {@code http} scheme and a default {@link ClientHttpConnector}.
*
* @return the resulting {@link WebClientProvider}.
*/
static WebClientProvider http() {
return create("http");
}
/**
* Creates a new {@link WebClientProvider} using the given {@code scheme} and a default {@link ClientHttpConnector}.
*
* @param scheme protocol scheme such as {@literal http} or {@literal https}.
* @return the resulting {@link WebClientProvider}.
*/
static WebClientProvider create(String scheme) {
Assert.hasText(scheme, "Protocol scheme must not be empty");
return new DefaultWebClientProvider(scheme, null);
}
/**
* Creates a new {@link WebClientProvider} given {@code scheme} and {@link ClientHttpConnector}.
*
* @param scheme protocol scheme such as {@literal http} or {@literal https}.
* @param connector the HTTP connector to use. Can be {@literal null}.
* @return the resulting {@link WebClientProvider}.
*/
static WebClientProvider create(String scheme, @Nullable ClientHttpConnector connector) {
Assert.hasText(scheme, "Protocol scheme must not be empty");
return new DefaultWebClientProvider(scheme, connector);
}
/**
* Obtain the {@link WebClient} configured with {@link #withDefaultHeaders(HttpHeaders) default HTTP headers} and
* {@link Consumer} error callback for a given {@link InetSocketAddress endpoint}.
*
* @return the {@link WebClient} for the given {@link InetSocketAddress endpoint}.
*/
WebClient get(InetSocketAddress endpoint);
/**
* Obtain the {@link HttpHeaders} to be used by default.
*
* @return never {@literal null}. {@link HttpHeaders#EMPTY} by default.
*/
HttpHeaders getDefaultHeaders();
/**
* Obtain the {@link Consumer error listener} to be used;
*
* @return never {@literal null}.
*/
Consumer<Throwable> getErrorListener();
/**
* Obtain the {@link String pathPrefix} to be used.
*
* @return the pathPrefix if set.
* @since 4.0
*/
@Nullable
String getPathPrefix();
/**
* Create a new instance of {@link WebClientProvider} applying the given headers by default.
*
* @param headers must not be {@literal null}.
* @return new instance of {@link WebClientProvider}.
*/
WebClientProvider withDefaultHeaders(HttpHeaders headers);
/**
* Create a new instance of {@link WebClientProvider} calling the given {@link Consumer} on error.
*
* @param errorListener must not be {@literal null}.
* @return new instance of {@link WebClientProvider}.
*/
WebClientProvider withErrorListener(Consumer<Throwable> errorListener);
/**
* Create a new instance of {@link WebClientProvider} where HTTP requests are called with the given path prefix.
*
* @param pathPrefix Path prefix to add to requests
* @return new instance of {@link WebClientProvider}
* @since 4.0
*/
WebClientProvider withPathPrefix(String pathPrefix);
/**
* Create a new instance of {@link WebClientProvider} calling the given {@link Function} to configure the
* {@link WebClient}.
*
* @param webClientConfigurer configuration function
* @return new instance of {@link WebClientProvider}
* @since 4.0
*/
WebClientProvider withWebClientConfigurer(Function<WebClient, WebClient> webClientConfigurer);
/**
* Create a new instance of {@link WebClientProvider} calling the given {@link Consumer} to configure the requests of
* this {@link WebClient}.
*
* @param requestConfigurer request configuration callback
* @return new instance of {@link WebClientProvider}
* @since 4.3
*/
WebClientProvider withRequestConfigurer(Consumer<WebClient.RequestHeadersSpec<?>> requestConfigurer);
/**
* Creates a {@link WebClientProvider} for the given configuration
*
* @param clientConfiguration must not be {@literal} null
* @return the {@link WebClientProvider}
* @since 4.3
*/
static WebClientProvider getWebClientProvider(ClientConfiguration clientConfiguration) {
Assert.notNull(clientConfiguration, "clientConfiguration must not be null");
Duration connectTimeout = clientConfiguration.getConnectTimeout();
Duration soTimeout = clientConfiguration.getSocketTimeout();
HttpClient httpClient = HttpClient.create().compress(true);
if (!connectTimeout.isNegative()) {
httpClient = httpClient.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, Math.toIntExact(connectTimeout.toMillis()));
}
if (!soTimeout.isNegative()) {
httpClient = httpClient.doOnConnected(connection -> connection //
.addHandlerLast(new ReadTimeoutHandler(soTimeout.toMillis(), TimeUnit.MILLISECONDS))
.addHandlerLast(new WriteTimeoutHandler(soTimeout.toMillis(), TimeUnit.MILLISECONDS)));
}
if (clientConfiguration.getProxy().isPresent()) {
String proxy = clientConfiguration.getProxy().get();
String[] hostPort = proxy.split(":");
if (hostPort.length != 2) {
throw new IllegalArgumentException("invalid proxy configuration " + proxy + ", should be \"host:port\"");
}
httpClient = httpClient.proxy(proxyOptions -> proxyOptions.type(ProxyProvider.Proxy.HTTP).host(hostPort[0])
.port(Integer.parseInt(hostPort[1])));
}
String scheme = "http";
if (clientConfiguration.useSsl()) {
Optional<SSLContext> sslContext = clientConfiguration.getSslContext();
if (sslContext.isPresent()) {
httpClient = httpClient
.secure(sslContextSpec -> sslContextSpec.sslContext(new JdkSslContext(sslContext.get(), true, null,
IdentityCipherSuiteFilter.INSTANCE, ApplicationProtocolConfig.DISABLED, ClientAuth.NONE, null, false)));
} else {
httpClient = httpClient.secure();
}
scheme = "https";
}
WebClientProvider provider = WebClientProvider.create(scheme, new ReactorClientHttpConnector(httpClient));
if (clientConfiguration.getPathPrefix() != null) {
provider = provider.withPathPrefix(clientConfiguration.getPathPrefix());
}
Function<WebClient, WebClient> webClientConfigurer = webClient -> {
for (ClientConfiguration.ClientConfigurationCallback<?> clientConfigurer : clientConfiguration
.getClientConfigurers()) {
if (clientConfigurer instanceof ReactiveRestClients.WebClientConfigurationCallback webClientConfigurationCallback) {
webClient = webClientConfigurationCallback.configure(webClient);
}
}
return webClient;
};
provider = provider //
.withDefaultHeaders(HttpHeaders.readOnlyHttpHeaders(clientConfiguration.getDefaultHeaders())) //
.withWebClientConfigurer(webClientConfigurer) //
.withRequestConfigurer(requestHeadersSpec -> requestHeadersSpec //
.headers(httpHeaders -> {
HttpHeaders suppliedHeaders = HttpHeaders
.readOnlyHttpHeaders(clientConfiguration.getHeadersSupplier().get());
if (suppliedHeaders != null && suppliedHeaders != HttpHeaders.EMPTY) {
httpHeaders.addAll(suppliedHeaders);
}
// this WebClientProvider is built with ES 7 and not used on 8 anymore
httpHeaders.add("Accept", "application/vnd.elasticsearch+json;compatible-with=7");
var contentTypeHeader = "Content-Type";
if (httpHeaders.containsKey(contentTypeHeader)) {
httpHeaders.remove(contentTypeHeader);
}
httpHeaders.add(contentTypeHeader, "application/vnd.elasticsearch+json;compatible-with=7");
}));
return provider;
}
}

View File

@ -1,25 +0,0 @@
/*
* Copyright 2022-2023 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.
*/
/**
* This package contains classes that use the old Elasticsearch 7 libraries to access Elasticsearch either directly by
* using the RestHighLevelClient or indirectly by using code copied from Elasticsearch libraries (reactive
* implementation). These classes are deprectaed in favour of using the implementations using the new Elasticsearch
* Client.
*/
@org.springframework.lang.NonNullApi
@org.springframework.lang.NonNullFields
package org.springframework.data.elasticsearch.client.erhlc;

View File

@ -27,7 +27,6 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.client.erhlc.AbstractReactiveElasticsearchConfiguration;
import org.springframework.data.elasticsearch.core.RefreshPolicy;
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
import org.springframework.data.elasticsearch.core.convert.ElasticsearchCustomConversions;
@ -144,8 +143,8 @@ public class ElasticsearchConfigurationSupport {
if (beanClassName != null) {
try {
initialEntitySet.add(
ClassUtils.forName(beanClassName, AbstractReactiveElasticsearchConfiguration.class.getClassLoader()));
initialEntitySet
.add(ClassUtils.forName(beanClassName, ElasticsearchConfigurationSupport.class.getClassLoader()));
} catch (ClassNotFoundException | LinkageError ignored) {}
}
}

View File

@ -1,241 +0,0 @@
/*
* Copyright 2019-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.elasticsearch.core;
import static org.springframework.util.StringUtils.*;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.elasticsearch.UncategorizedElasticsearchException;
import org.springframework.data.elasticsearch.annotations.Mapping;
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
import org.springframework.data.elasticsearch.core.document.Document;
import org.springframework.data.elasticsearch.core.index.AliasData;
import org.springframework.data.elasticsearch.core.index.MappingBuilder;
import org.springframework.data.elasticsearch.core.index.Settings;
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentEntity;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
* Base implementation of {@link IndexOperations} common to Transport and Rest based Implementations of IndexOperations.
*
* @author Peter-Josef Meisch
* @author Sascha Woo
* @since 4.0
*/
public abstract class AbstractIndexTemplate implements IndexOperations {
protected final ElasticsearchConverter elasticsearchConverter;
@Nullable protected final Class<?> boundClass;
@Nullable private final IndexCoordinates boundIndex;
public AbstractIndexTemplate(ElasticsearchConverter elasticsearchConverter, Class<?> boundClass) {
Assert.notNull(boundClass, "boundClass may not be null");
this.elasticsearchConverter = elasticsearchConverter;
this.boundClass = boundClass;
this.boundIndex = null;
}
public AbstractIndexTemplate(ElasticsearchConverter elasticsearchConverter, IndexCoordinates boundIndex) {
Assert.notNull(boundIndex, "boundIndex may not be null");
this.elasticsearchConverter = elasticsearchConverter;
this.boundClass = null;
this.boundIndex = boundIndex;
}
protected Class<?> checkForBoundClass() {
if (boundClass == null) {
throw new InvalidDataAccessApiUsageException("IndexOperations are not bound");
}
return boundClass;
}
// region IndexOperations
@Override
public boolean create() {
Settings settings = boundClass != null ? createSettings(boundClass) : new Settings();
return doCreate(getIndexCoordinates(), settings, null);
}
@Override
public Settings createSettings(Class<?> clazz) {
Assert.notNull(clazz, "clazz must not be null");
ElasticsearchPersistentEntity<?> persistentEntity = getRequiredPersistentEntity(clazz);
String settingPath = persistentEntity.settingPath();
return hasText(settingPath) //
? Settings.parse(ResourceUtil.readFileFromClasspath(settingPath)) //
: persistentEntity.getDefaultSettings();
}
@Override
public boolean createWithMapping() {
return doCreate(getIndexCoordinates(), createSettings(), createMapping());
}
@Override
public boolean create(Map<String, Object> settings) {
Assert.notNull(settings, "settings must not be null");
return doCreate(getIndexCoordinates(), settings, null);
}
@Override
public boolean create(Map<String, Object> settings, Document mapping) {
Assert.notNull(settings, "settings must not be null");
Assert.notNull(mapping, "mapping must not be null");
return doCreate(getIndexCoordinates(), settings, mapping);
}
protected abstract boolean doCreate(IndexCoordinates index, Map<String, Object> settings, @Nullable Document mapping);
@Override
public boolean delete() {
return doDelete(getIndexCoordinates());
}
protected abstract boolean doDelete(IndexCoordinates index);
@Override
public boolean exists() {
return doExists(getIndexCoordinates());
}
protected abstract boolean doExists(IndexCoordinates index);
@Override
public boolean putMapping(Document mapping) {
return doPutMapping(getIndexCoordinates(), mapping);
}
protected abstract boolean doPutMapping(IndexCoordinates index, Document mapping);
@Override
public Map<String, Object> getMapping() {
return doGetMapping(getIndexCoordinates());
}
abstract protected Map<String, Object> doGetMapping(IndexCoordinates index);
@Override
public Settings getSettings() {
return getSettings(false);
}
@Override
public Settings getSettings(boolean includeDefaults) {
return doGetSettings(getIndexCoordinates(), includeDefaults);
}
protected abstract Settings doGetSettings(IndexCoordinates index, boolean includeDefaults);
@Override
public void refresh() {
doRefresh(getIndexCoordinates());
}
protected abstract void doRefresh(IndexCoordinates indexCoordinates);
@Override
public Map<String, Set<AliasData>> getAliases(String... aliasNames) {
Assert.notEmpty(aliasNames, "aliasNames must not be empty");
return doGetAliases(aliasNames, null);
}
@Override
public Map<String, Set<AliasData>> getAliasesForIndex(String... indexNames) {
Assert.notEmpty(indexNames, "indexNames must not be empty");
return doGetAliases(null, indexNames);
}
protected abstract Map<String, Set<AliasData>> doGetAliases(@Nullable String[] aliasNames,
@Nullable String[] indexNames);
@Override
public Document createMapping() {
return createMapping(checkForBoundClass());
}
@Override
public Document createMapping(Class<?> clazz) {
// load mapping specified in Mapping annotation if present
// noinspection DuplicatedCode
Mapping mappingAnnotation = AnnotatedElementUtils.findMergedAnnotation(clazz, Mapping.class);
if (mappingAnnotation != null) {
String mappingPath = mappingAnnotation.mappingPath();
if (hasText(mappingPath)) {
String mappings = ResourceUtil.readFileFromClasspath(mappingPath);
if (hasText(mappings)) {
return Document.parse(mappings);
}
}
}
// build mapping from field annotations
try {
String mapping = new MappingBuilder(elasticsearchConverter).buildPropertyMapping(clazz);
return Document.parse(mapping);
} catch (Exception e) {
throw new UncategorizedElasticsearchException("Failed to build mapping for " + clazz.getSimpleName(), e);
}
}
@Override
public Settings createSettings() {
return createSettings(checkForBoundClass());
}
// endregion
// region Helper functions
ElasticsearchPersistentEntity<?> getRequiredPersistentEntity(Class<?> clazz) {
return elasticsearchConverter.getMappingContext().getRequiredPersistentEntity(clazz);
}
@Override
public IndexCoordinates getIndexCoordinates() {
return (boundClass != null) ? getIndexCoordinatesFor(boundClass) : Objects.requireNonNull(boundIndex);
}
public IndexCoordinates getIndexCoordinatesFor(Class<?> clazz) {
return getRequiredPersistentEntity(clazz).getIndexCoordinates();
}
// endregion
}

View File

@ -15,8 +15,6 @@
*/
package org.springframework.data.elasticsearch.core;
import org.reactivestreams.Publisher;
import org.springframework.data.elasticsearch.client.erhlc.ReactiveElasticsearchClient;
import org.springframework.data.elasticsearch.core.cluster.ReactiveClusterOperations;
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentEntity;
@ -35,37 +33,6 @@ import org.springframework.lang.Nullable;
public interface ReactiveElasticsearchOperations
extends ReactiveDocumentOperations, ReactiveSearchOperations, ReactiveScriptOperations {
/**
* Execute within a {@link ClientCallback} managing resources and translating errors.
*
* @param callback must not be {@literal null}.
* @param <T> the type the Publisher emits
* @return the {@link Publisher} emitting results.
* @deprecated since 4.4, use the execute methods from the implementing classes (they are client specific)
*/
@Deprecated
<T> Publisher<T> execute(ClientCallback<Publisher<T>> callback);
/**
* Execute within a {@link IndicesClientCallback} managing resources and translating errors.
*
* @param callback must not be {@literal null}.
* @param <T> the type the Publisher emits
* @return the {@link Publisher} emitting results.
* @since 4.1
*/
<T> Publisher<T> executeWithIndicesClient(IndicesClientCallback<Publisher<T>> callback);
/**
* Execute within a {@link ClusterClientCallback} managing resources and translating errors.
*
* @param callback must not be {@literal null}.
* @param <T> the type the Publisher emits
* @return the {@link Publisher} emitting results.
* @since 4.1
*/
<T> Publisher<T> executeWithClusterClient(ClusterClientCallback<Publisher<T>> callback);
/**
* Get the {@link ElasticsearchConverter} used.
*
@ -120,39 +87,4 @@ public interface ReactiveElasticsearchOperations
*/
ReactiveElasticsearchOperations withRouting(RoutingResolver routingResolver);
// endregion
/**
* Callback interface to be used with {@link #execute(ClientCallback)} for operating directly on
* {@link ReactiveElasticsearchClient}.
*
* @param <T> result type of the callback
* @author Christoph Strobl
* @since 3.2
*/
interface ClientCallback<T extends Publisher<?>> {
T doWithClient(ReactiveElasticsearchClient client);
}
/**
* Callback interface to be used with {@link #executeWithIndicesClient(IndicesClientCallback)} for operating directly
* on {@link ReactiveElasticsearchClient.Indices}.
*
* @param <T> the return type
* @since 4.1
*/
interface IndicesClientCallback<T extends Publisher<?>> {
T doWithClient(ReactiveElasticsearchClient.Indices client);
}
/**
* Callback interface to be used with {@link #executeWithClusterClient(ClusterClientCallback)} for operating directly
* on {@link ReactiveElasticsearchClient.Cluster}.
*
* @param <T> the return type
* @since 4.2
*/
interface ClusterClientCallback<T extends Publisher<?>> {
T doWithClient(ReactiveElasticsearchClient.Cluster client);
}
}

View File

@ -21,9 +21,8 @@ import reactor.core.publisher.Mono;
import java.time.Duration;
import java.util.List;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.client.erhlc.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.BaseQueryBuilder;
import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.data.elasticsearch.core.suggest.response.Suggest;
@ -68,10 +67,7 @@ public interface ReactiveSearchOperations {
Mono<Long> count(Query query, Class<?> entityType, IndexCoordinates index);
/**
* Search the index for entities matching the given {@link Query query}. <br />
* {@link Pageable#isUnpaged() Unpaged} queries may overrule elasticsearch server defaults for page size by either
* delegating to the scroll API or using a max {@link org.elasticsearch.search.builder.SearchSourceBuilder#size(int)
* size}.
* Search the index for entities matching the given {@link Query query}.
*
* @param query must not be {@literal null}.
* @param entityType must not be {@literal null}.
@ -83,10 +79,7 @@ public interface ReactiveSearchOperations {
}
/**
* Search the index for entities matching the given {@link Query query}. <br />
* {@link Pageable#isUnpaged() Unpaged} queries may overrule elasticsearch server defaults for page size by either *
* delegating to the scroll API or using a max {@link org.elasticsearch.search.builder.SearchSourceBuilder#size(int) *
* size}.
* Search the index for entities matching the given {@link Query query}.
*
* @param query must not be {@literal null}.
* @param entityType The entity type for mapping the query. Must not be {@literal null}.
@ -250,8 +243,7 @@ public interface ReactiveSearchOperations {
/**
* Does a suggest query.
*
* @param query the Query containing the suggest definition. Must be currently a {@link NativeSearchQuery}, must not
* be {@literal null}.
* @param query the Query containing the suggest definition. Must not be {@literal null}.
* @param entityType the type of the entities that might be returned for a completion suggestion, must not be
* {@literal null}.
* @return suggest data
@ -262,8 +254,7 @@ public interface ReactiveSearchOperations {
/**
* Does a suggest query.
*
* @param query the Query containing the suggest definition. Must be currently a {@link NativeSearchQuery}, must not
* be {@literal null}.
* @param query the Query containing the suggest definition. Must not be {@literal null}.
* @param entityType the type of the entities that might be returned for a completion suggestion, must not be
* {@literal null}.
* @param index the index to run the query against, must not be {@literal null}.
@ -323,5 +314,15 @@ public interface ReactiveSearchOperations {
* @since 4.3
*/
Query idsQuery(List<String> ids);
/**
* Creates a {@link BaseQueryBuilder} that has the given ids setto the parameter value. No other properties of the
* bulder are set.
*
* @param ids the list of ids must not be {@literal null}
* @return query returning the documents with the given ids
* @since 5.2
*/
BaseQueryBuilder queryBuilderWithIds(List<String> ids);
// endregion
}

View File

@ -19,6 +19,7 @@ import java.time.Duration;
import java.util.List;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.BaseQueryBuilder;
import org.springframework.data.elasticsearch.core.query.MoreLikeThisQuery;
import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.lang.Nullable;
@ -231,6 +232,15 @@ public interface SearchOperations {
*/
Query idsQuery(List<String> ids);
/**
* Creates a {@link BaseQueryBuilder} that has the given ids setto the parameter value. No other properties of the bulder are set.
*
* @param ids the list of ids must not be {@literal null}
* @return query returning the documents with the given ids
* @since 5.2
*/
BaseQueryBuilder queryBuilderWithIds(List<String> ids);
/**
* Opens a point in time (pit) in Elasticsearch.
*

View File

@ -26,6 +26,11 @@ import org.springframework.data.domain.Sort;
* @author Peter-Josef Meisch
*/
public class StringQuery extends BaseQuery {
public static String MATCH_ALL = """
{
"match_all": {}
}
""";
private final String source;

View File

@ -1,47 +0,0 @@
/*
* Copyright 2020-2023 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.assertj.core.api.Assertions.*;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.elasticsearch.core.ReactiveElasticsearchOperations;
import org.springframework.data.elasticsearch.junit.jupiter.ElasticsearchRestTemplateConfiguration;
import org.springframework.data.elasticsearch.junit.jupiter.ReactiveElasticsearchRestTemplateConfiguration;
import org.springframework.data.elasticsearch.junit.jupiter.SpringIntegrationTest;
import org.springframework.test.context.ContextConfiguration;
/**
* class demonstrating the setup of a JUnit 5 test in Spring Data Elasticsearch that uses the reactive rest client. The
* ContextConfiguration must include the {@link ElasticsearchRestTemplateConfiguration} class.
*
* @author Peter-Josef Meisch
*/
@SpringIntegrationTest
@ContextConfiguration(classes = { ReactiveElasticsearchRestTemplateConfiguration.class })
@DisplayName("a sample JUnit 5 test with reactive rest client")
public class JUnit5SampleReactiveERHLCTests {
@Autowired private ReactiveElasticsearchOperations elasticsearchOperations;
@Test
@DisplayName("should have a ReactiveElasticsearchOperations")
void shouldHaveARestTemplate() {
assertThat(elasticsearchOperations).isNotNull().isInstanceOf(ReactiveElasticsearchOperations.class);
}
}

View File

@ -1,47 +0,0 @@
/*
* Copyright 2019-2023 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.assertj.core.api.Assertions.*;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.data.elasticsearch.client.erhlc.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.junit.jupiter.ElasticsearchRestTemplateConfiguration;
import org.springframework.data.elasticsearch.junit.jupiter.SpringIntegrationTest;
import org.springframework.test.context.ContextConfiguration;
/**
* class demonstrating the setup of a JUnit 5 test in Spring Data Elasticsearch that uses the rest client. The
* ContextConfiguration must include the {@link ElasticsearchRestTemplateConfiguration} class.
*
* @author Peter-Josef Meisch
*/
@SpringIntegrationTest
@ContextConfiguration(classes = { ElasticsearchRestTemplateConfiguration.class })
@DisplayName("a sample JUnit 5 test with rest client")
public class JUnit5SampleRestTemplateTests {
@Autowired private ElasticsearchOperations elasticsearchOperations;
@Test
@DisplayName("should have a ElasticsearchRestTemplate")
void shouldHaveARestTemplate() {
assertThat(elasticsearchOperations).isNotNull().isInstanceOf(ElasticsearchRestTemplate.class);
}
}

View File

@ -1,89 +0,0 @@
/*
* Copyright 2022-2023 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.elasticsearch.index.query.QueryBuilders.*;
import org.apache.lucene.search.join.ScoreMode;
import org.jetbrains.annotations.NotNull;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.elasticsearch.client.erhlc.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.data.elasticsearch.junit.jupiter.ElasticsearchRestTemplateConfiguration;
import org.springframework.data.elasticsearch.utils.IndexNameProvider;
import org.springframework.test.context.ContextConfiguration;
/**
* @author Peter-Josef Meisch
*/
@ContextConfiguration(classes = { NestedObjectERHLCIntegrationTests.Config.class })
public class NestedObjectERHLCIntegrationTests extends NestedObjectIntegrationTests {
@Configuration
@Import({ ElasticsearchRestTemplateConfiguration.class })
static class Config {
@Bean
IndexNameProvider indexNameProvider() {
return new IndexNameProvider("nestedobject-es7");
}
}
@NotNull
protected Query getNestedQuery1() {
return new NativeSearchQueryBuilder().withQuery( //
nestedQuery("car", //
boolQuery() //
.must(termQuery("car.name", "saturn")) //
.must(termQuery("car.model", "imprezza")), //
ScoreMode.None)) //
.build();
}
@NotNull
protected Query getNestedQuery2() {
return new NativeSearchQueryBuilder().withQuery( //
boolQuery() //
.must(nestedQuery("girlFriends", //
termQuery("girlFriends.type", "temp"), //
ScoreMode.None)) //
.must(nestedQuery("girlFriends.cars", //
termQuery("girlFriends.cars.name", "Ford".toLowerCase()), //
ScoreMode.None))) //
.build();
}
@NotNull
protected Query getNestedQuery3() {
return new NativeSearchQueryBuilder().withQuery( //
nestedQuery("books", //
boolQuery() //
.must(termQuery("books.name", "java")), //
ScoreMode.None))//
.build();
}
@NotNull
protected Query getNestedQuery4() {
return new NativeSearchQueryBuilder().withQuery( //
nestedQuery("buckets", //
termQuery("buckets.1", "test3"), //
ScoreMode.None)) //
.build();
}
}

View File

@ -22,18 +22,13 @@ import static org.springframework.data.elasticsearch.support.HttpHeaders.*;
import java.net.InetSocketAddress;
import java.time.Duration;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import javax.net.ssl.SSLContext;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.impl.nio.client.HttpAsyncClientBuilder;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.data.elasticsearch.client.erhlc.ReactiveRestClients;
import org.springframework.data.elasticsearch.client.erhlc.RestClients;
import org.springframework.data.elasticsearch.support.HttpHeaders;
import org.springframework.web.reactive.function.client.WebClient;
/**
* Unit tests for {@link ClientConfiguration}.
@ -153,48 +148,6 @@ public class ClientConfigurationUnitTests {
assertThat(clientConfiguration.getHostNameVerifier()).contains(NoopHostnameVerifier.INSTANCE);
}
@Test // #1885
@DisplayName("should use configured httpClientConfigurer as client configurer")
void shouldUseConfiguredHttpClientConfigurerAsClientConfigurer() {
AtomicInteger callCounter = new AtomicInteger();
ClientConfiguration clientConfiguration = ClientConfiguration.builder() //
.connectedTo("foo", "bar") //
.withClientConfigurer(RestClients.RestClientConfigurationCallback.from(httpClientBuilder -> {
callCounter.incrementAndGet();
return httpClientBuilder;
})) //
.build();
ClientConfiguration.ClientConfigurationCallback<?> clientConfigurer = clientConfiguration.getClientConfigurers()
.get(0);
((RestClients.RestClientConfigurationCallback) clientConfigurer).configure(HttpAsyncClientBuilder.create());
assertThat(callCounter.get()).isEqualTo(1);
}
@Test // #1885
@DisplayName("should use configured webClientConfigurer as client configurer")
void shouldUseConfiguredWebClientConfigurerAsClientConfigurer() {
AtomicInteger callCounter = new AtomicInteger();
ClientConfiguration clientConfiguration = ClientConfiguration.builder() //
.connectedTo("foo", "bar") //
.withClientConfigurer(ReactiveRestClients.WebClientConfigurationCallback.from(webClient -> {
callCounter.incrementAndGet();
return webClient;
})) //
.build();
ClientConfiguration.ClientConfigurationCallback<?> clientConfigurer = clientConfiguration.getClientConfigurers()
.get(0);
((ReactiveRestClients.WebClientConfigurationCallback) clientConfigurer).configure(WebClient.builder().build());
assertThat(callCounter.get()).isEqualTo(1);
}
@Test // #1885
@DisplayName("should use configured client configurer")
void shouldUseConfiguredClientConfigurer() {

View File

@ -17,9 +17,9 @@ package org.springframework.data.elasticsearch.client;
import static com.github.tomakehurst.wiremock.client.WireMock.*;
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
import static io.specto.hoverfly.junit.dsl.HoverflyDsl.*;
import static io.specto.hoverfly.junit.verification.HoverflyVerifications.*;
import static org.assertj.core.api.Assertions.*;
import static io.specto.hoverfly.junit.dsl.HoverflyDsl.service;
import static io.specto.hoverfly.junit.verification.HoverflyVerifications.atLeast;
import static org.assertj.core.api.Assertions.assertThat;
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.transport.endpoints.BooleanResponse;
@ -34,18 +34,12 @@ import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
import java.util.stream.Stream;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.xcontent.XContentType;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import org.springframework.data.elasticsearch.client.elc.ElasticsearchClients;
import org.springframework.data.elasticsearch.client.elc.ReactiveElasticsearchClient;
import org.springframework.data.elasticsearch.client.erhlc.ReactiveRestClients;
import org.springframework.data.elasticsearch.client.erhlc.RestClients;
import org.springframework.data.elasticsearch.support.HttpHeaders;
import com.github.tomakehurst.wiremock.WireMockServer;
@ -118,18 +112,7 @@ public class RestClientsTest {
return httpHeaders;
});
if (clientUnderTestFactory instanceof ERHLCUnderTestFactory) {
configurationBuilder
.withClientConfigurer(RestClients.RestClientConfigurationCallback.from(httpClientBuilder -> {
httpClientConfigurerCount.incrementAndGet();
return httpClientBuilder;
}));
} else if (clientUnderTestFactory instanceof ReactiveERHLCUnderTestFactory) {
configurationBuilder.withClientConfigurer(ReactiveRestClients.WebClientConfigurationCallback.from(webClient -> {
httpClientConfigurerCount.incrementAndGet();
return webClient;
}));
} else if (clientUnderTestFactory instanceof ELCUnderTestFactory) {
if (clientUnderTestFactory instanceof ELCUnderTestFactory) {
configurationBuilder.withClientConfigurer(
ElasticsearchClients.ElasticsearchHttpClientConfigurationCallback.from(httpClientBuilder -> {
httpClientConfigurerCount.incrementAndGet();
@ -203,9 +186,9 @@ public class RestClientsTest {
"result": "created"
}
""" //
, 201) //
.withHeader("Content-Type", "application/vnd.elasticsearch+json;compatible-with=7") //
.withHeader("X-Elastic-Product", "Elasticsearch")));
, 201) //
.withHeader("Content-Type", "application/vnd.elasticsearch+json;compatible-with=7") //
.withHeader("X-Elastic-Product", "Elasticsearch")));
ClientConfigurationBuilder configurationBuilder = new ClientConfigurationBuilder();
configurationBuilder //
@ -349,42 +332,6 @@ public class RestClientsTest {
}
}
/**
* {@link ClientUnderTestFactory} implementation for the old {@link RestHighLevelClient}.
*/
@Deprecated
static class ERHLCUnderTestFactory extends ClientUnderTestFactory {
@Override
protected String getDisplayName() {
return "RestHighLevelClient";
}
@Override
ClientUnderTest create(ClientConfiguration clientConfiguration) {
RestHighLevelClient client = RestClients.create(clientConfiguration).rest();
return new ClientUnderTest() {
@Override
public boolean ping() throws Exception {
return client.ping(RequestOptions.DEFAULT);
}
@Override
public boolean usesInitialRequest() {
return true;
}
@Override
public <T> void index(T entity) throws IOException {
IndexRequest indexRequest = new IndexRequest("index");
indexRequest.id("42");
indexRequest.source(entity, XContentType.JSON);
client.index(indexRequest, RequestOptions.DEFAULT);
}
};
}
}
/**
* {@link ClientUnderTestFactory} implementation for the {@link co.elastic.clients.elasticsearch.ElasticsearchClient}.
*/
@ -423,44 +370,6 @@ public class RestClientsTest {
}
}
/**
* {@link ClientUnderTestFactory} implementation for the
* {@link org.springframework.data.elasticsearch.client.erhlc.ReactiveElasticsearchClient}.
*/
@Deprecated
static class ReactiveERHLCUnderTestFactory extends ClientUnderTestFactory {
@Override
protected String getDisplayName() {
return "ReactiveElasticsearchClient (RHLC based)";
}
@Override
ClientUnderTest create(ClientConfiguration clientConfiguration) {
org.springframework.data.elasticsearch.client.erhlc.ReactiveElasticsearchClient client = ReactiveRestClients
.create(clientConfiguration);
return new ClientUnderTest() {
@Override
public boolean ping() throws Exception {
return Boolean.TRUE.equals(client.ping().block());
}
@Override
public boolean usesInitialRequest() {
return true;
}
@Override
public <T> void index(T entity) throws IOException {
IndexRequest indexRequest = new IndexRequest("index");
indexRequest.id("42");
indexRequest.source(entity, XContentType.JSON);
client.index(indexRequest).block();
}
};
}
}
/**
* {@link ClientUnderTestFactory} implementation for the {@link ReactiveElasticsearchClient}.
*/
@ -506,8 +415,6 @@ public class RestClientsTest {
*/
static Stream<ClientUnderTestFactory> clientUnderTestFactorySource() {
return Stream.of( //
new ERHLCUnderTestFactory(), //
new ReactiveERHLCUnderTestFactory(), //
new ELCUnderTestFactory(), //
new ReactiveELCUnderTestFactory());
}

View File

@ -1,521 +0,0 @@
/*
* Copyright 2019-2023 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.erhlc;
import static org.skyscreamer.jsonassert.JSONAssert.*;
import java.time.LocalDate;
import java.util.Base64;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import org.assertj.core.api.SoftAssertions;
import org.json.JSONException;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.core.convert.support.GenericConversionService;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.DateFormat;
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.convert.GeoConverters;
import org.springframework.data.elasticsearch.core.convert.MappingElasticsearchConverter;
import org.springframework.data.elasticsearch.core.document.Document;
import org.springframework.data.elasticsearch.core.geo.GeoJson;
import org.springframework.data.elasticsearch.core.geo.GeoJsonPoint;
import org.springframework.data.elasticsearch.core.mapping.SimpleElasticsearchMappingContext;
import org.springframework.data.elasticsearch.core.query.Criteria;
import org.springframework.data.elasticsearch.core.query.CriteriaQuery;
import org.springframework.data.elasticsearch.core.query.FetchSourceFilterBuilder;
import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.data.elasticsearch.core.query.SourceFilter;
import org.springframework.lang.Nullable;
/**
* Tests for the mapping of {@link CriteriaQuery} by a
* {@link org.springframework.data.elasticsearch.core.convert.MappingElasticsearchConverter}. In the same package as
* {@link CriteriaQueryProcessor} as this is needed to get the String representation to assert.
*
* @author Peter-Josef Meisch
* @author Sascha Woo
* @author vdisk
*/
public class CriteriaQueryMappingUnitTests {
MappingElasticsearchConverter mappingElasticsearchConverter;
// region setup
@BeforeEach
void setUp() {
SimpleElasticsearchMappingContext mappingContext = new SimpleElasticsearchMappingContext();
mappingContext.setInitialEntitySet(Collections.singleton(Person.class));
mappingContext.afterPropertiesSet();
mappingElasticsearchConverter = new MappingElasticsearchConverter(mappingContext, new GenericConversionService());
mappingElasticsearchConverter.afterPropertiesSet();
}
// endregion
// region tests
@Test // DATAES-716
void shouldMapNamesAndConvertValuesInCriteriaQuery() throws JSONException {
// use POJO properties and types in the query building
CriteriaQuery criteriaQuery = new CriteriaQuery( //
new Criteria("birthDate") //
.between(LocalDate.of(1989, 11, 9), LocalDate.of(1990, 11, 9)) //
.or("birthDate").is(LocalDate.of(2019, 12, 28)) //
);
// mapped field name and converted parameter
String expected = '{' + //
" \"bool\" : {" + //
" \"should\" : [" + //
" {" + //
" \"range\" : {" + //
" \"birth-date\" : {" + //
" \"from\" : \"09.11.1989\"," + //
" \"to\" : \"09.11.1990\"," + //
" \"include_lower\" : true," + //
" \"include_upper\" : true" + //
" }" + //
" }" + //
" }," + //
" {" + //
" \"query_string\" : {" + //
" \"query\" : \"28.12.2019\"," + //
" \"fields\" : [" + //
" \"birth-date^1.0\"" + //
" ]" + //
" }" + //
" }" + //
" ]" + //
" }" + //
'}'; //
mappingElasticsearchConverter.updateQuery(criteriaQuery, Person.class);
String queryString = new CriteriaQueryProcessor().createQuery(criteriaQuery.getCriteria()).toString();
assertEquals(expected, queryString, false);
}
@Test // #1668
void shouldMapNamesAndConvertValuesInCriteriaQueryForSubCriteria() throws JSONException {
// use POJO properties and types in the query building
CriteriaQuery criteriaQuery = new CriteriaQuery( //
Criteria.or().subCriteria(Criteria.where("birthDate") //
.between(LocalDate.of(1989, 11, 9), LocalDate.of(1990, 11, 9))) //
.subCriteria(Criteria.where("birthDate").is(LocalDate.of(2019, 12, 28))) //
);
// mapped field name and converted parameter
String expected = """
{
"bool" : {
"should" : [
{
"bool" : {
"must" : [
{
"range" : {
"birth-date" : {
"from" : "09.11.1989",
"to" : "09.11.1990",
"include_lower" : true,
"include_upper" : true,
"boost" : 1.0
}
}
}
],
"adjust_pure_negative" : true,
"boost" : 1.0
}
},
{
"bool" : {
"must" : [
{
"query_string" : {
"query" : "28.12.2019",
"fields" : [
"birth-date^1.0"
],
"type" : "best_fields",
"default_operator" : "and",
"max_determinized_states" : 10000,
"enable_position_increments" : true,
"fuzziness" : "AUTO",
"fuzzy_prefix_length" : 0,
"fuzzy_max_expansions" : 50,
"phrase_slop" : 0,
"escape" : false,
"auto_generate_synonyms_phrase_query" : true,
"fuzzy_transpositions" : true,
"boost" : 1.0
}
}
],
"adjust_pure_negative" : true,
"boost" : 1.0
}
}
],
"adjust_pure_negative" : true,
"boost" : 1.0
}
}"""; //
mappingElasticsearchConverter.updateQuery(criteriaQuery, Person.class);
String queryString = new CriteriaQueryProcessor().createQuery(criteriaQuery.getCriteria()).toString();
assertEquals(expected, queryString, false);
}
@Test // #1668
void shouldMapNamesAndConvertValuesInCriteriaQueryForSubCriteriaWithDate() throws JSONException {
// use POJO properties and types in the query building
CriteriaQuery criteriaQuery = new CriteriaQuery( //
Criteria.or().subCriteria(Criteria.where("birthDate") //
.between(LocalDate.of(1989, 11, 9), LocalDate.of(1990, 11, 9))) //
.subCriteria(Criteria.where("createdDate").is(new Date(383745721653L))) //
);
// mapped field name and converted parameter
String expected = """
{
"bool" : {
"should" : [
{
"bool" : {
"must" : [
{
"range" : {
"birth-date" : {
"from" : "09.11.1989",
"to" : "09.11.1990",
"include_lower" : true,
"include_upper" : true,
"boost" : 1.0
}
}
}
],
"adjust_pure_negative" : true,
"boost" : 1.0
}
},
{
"bool" : {
"must" : [
{
"query_string" : {
"query" : "383745721653",
"fields" : [
"created-date^1.0"
],
"type" : "best_fields",
"default_operator" : "and",
"max_determinized_states" : 10000,
"enable_position_increments" : true,
"fuzziness" : "AUTO",
"fuzzy_prefix_length" : 0,
"fuzzy_max_expansions" : 50,
"phrase_slop" : 0,
"escape" : false,
"auto_generate_synonyms_phrase_query" : true,
"fuzzy_transpositions" : true,
"boost" : 1.0
}
}
],
"adjust_pure_negative" : true,
"boost" : 1.0
}
}
],
"adjust_pure_negative" : true,
"boost" : 1.0
}
}"""; //
mappingElasticsearchConverter.updateQuery(criteriaQuery, Person.class);
String queryString = new CriteriaQueryProcessor().createQuery(criteriaQuery.getCriteria()).toString();
assertEquals(expected, queryString, false);
}
@Test // DATAES-706
void shouldMapNamesAndValuesInSubCriteriaQuery() throws JSONException {
CriteriaQuery criteriaQuery = new CriteriaQuery( //
new Criteria("firstName").matches("John") //
.subCriteria(new Criteria("birthDate") //
.between(LocalDate.of(1989, 11, 9), LocalDate.of(1990, 11, 9)) //
.or("birthDate").is(LocalDate.of(2019, 12, 28))));
String expected = """
{
"bool": {
"must": [
{
"match": {
"first-name": {
"query": "John"
}
}
},
{
"bool": {
"should": [
{
"range": {
"birth-date": {
"from": "09.11.1989",
"to": "09.11.1990",
"include_lower": true,
"include_upper": true
}
}
},
{
"query_string": {
"query": "28.12.2019",
"fields": [
"birth-date^1.0"
]
}
}
]
}
}
]
}
}
"""; //
mappingElasticsearchConverter.updateQuery(criteriaQuery, Person.class);
String queryString = new CriteriaQueryProcessor().createQuery(criteriaQuery.getCriteria()).toString();
assertEquals(expected, queryString, false);
}
@Test // DATAES-931
@DisplayName("should map names in GeoJson query")
void shouldMapNamesInGeoJsonQuery() throws JSONException {
GeoJsonPoint geoJsonPoint = GeoJsonPoint.of(1.2, 3.4);
CriteriaQuery criteriaQuery = new CriteriaQuery(new Criteria("geoShapeField").intersects(geoJsonPoint));
String base64Query = getBase64EncodedGeoShapeQuery(geoJsonPoint, "geo-shape-field", "intersects");
String expected = "{\n" + //
" \"wrapper\": {\n" + //
" \"query\": \"" + base64Query + "\"\n" + //
" }\n" + //
"}\n"; //
mappingElasticsearchConverter.updateQuery(criteriaQuery, GeoShapeEntity.class);
String queryString = new CriteriaFilterProcessor().createFilter(criteriaQuery.getCriteria()).toString();
assertEquals(expected, queryString, false);
}
@Test // #1753
@DisplayName("should map names and value in nested entities")
void shouldMapNamesAndValueInNestedEntities() throws JSONException {
String expected = """
{
"bool": {
"must": [
{
"nested": {
"query": {
"query_string": {
"query": "03.10.1999",
"fields": [
"per-sons.birth-date^1.0"
]
}
},
"path": "per-sons"
}
}
]
}
}
"""; //
CriteriaQuery criteriaQuery = new CriteriaQuery(new Criteria("persons.birthDate").is(LocalDate.of(1999, 10, 3)));
mappingElasticsearchConverter.updateQuery(criteriaQuery, House.class);
String queryString = new CriteriaQueryProcessor().createQuery(criteriaQuery.getCriteria()).toString();
assertEquals(expected, queryString, false);
}
@Test // #1753
@DisplayName("should map names and value in nested entities with sub-fields")
void shouldMapNamesAndValueInNestedEntitiesWithSubfields() throws JSONException {
String expected = """
{
"bool": {
"must": [
{
"nested": {
"query": {
"query_string": {
"query": "Foobar",
"fields": [
"per-sons.nick-name.keyword^1.0"
]
}
},
"path": "per-sons"
}
}
]
}
}
"""; //
CriteriaQuery criteriaQuery = new CriteriaQuery(new Criteria("persons.nickName.keyword").is("Foobar"));
mappingElasticsearchConverter.updateQuery(criteriaQuery, House.class);
String queryString = new CriteriaQueryProcessor().createQuery(criteriaQuery.getCriteria()).toString();
assertEquals(expected, queryString, false);
}
@Test // #1761
@DisplayName("should map names and value in object entities")
void shouldMapNamesAndValueInObjectEntities() throws JSONException {
String expected = """
{
"bool": {
"must": [
{
"query_string": {
"query": "03.10.1999",
"fields": [
"per-sons.birth-date^1.0"
]
}
}
]
}
}
"""; //
CriteriaQuery criteriaQuery = new CriteriaQuery(new Criteria("persons.birthDate").is(LocalDate.of(1999, 10, 3)));
mappingElasticsearchConverter.updateQuery(criteriaQuery, ObjectWithPerson.class);
String queryString = new CriteriaQueryProcessor().createQuery(criteriaQuery.getCriteria()).toString();
assertEquals(expected, queryString, false);
}
@Test // #1778
@DisplayName("should map names in source fields and SourceFilters")
void shouldMapNamesInSourceFieldsAndSourceFilters() {
Query query = Query.findAll();
// Note: we don't care if these filters make sense here, this test is only about name mapping
query.addFields("firstName", "lastName");
query.addSourceFilter(new FetchSourceFilterBuilder().withIncludes("firstName").withExcludes("lastName").build());
mappingElasticsearchConverter.updateQuery(query, Person.class);
SoftAssertions softly = new SoftAssertions();
softly.assertThat(query.getFields()).containsExactly("first-name", "last-name");
SourceFilter sourceFilter = query.getSourceFilter();
softly.assertThat(sourceFilter).isNotNull();
softly.assertThat(sourceFilter.getIncludes()).containsExactly("first-name");
softly.assertThat(sourceFilter.getExcludes()).containsExactly("last-name");
softly.assertAll();
}
@Test
@DisplayName("should map names in source stored fields")
void shouldMapNamesInSourceStoredFields() {
Query query = Query.findAll();
query.addStoredFields("firstName", "lastName");
mappingElasticsearchConverter.updateQuery(query, Person.class);
SoftAssertions softly = new SoftAssertions();
List<String> storedFields = query.getStoredFields();
softly.assertThat(storedFields).isNotNull();
softly.assertThat(storedFields).containsExactly("first-name", "last-name");
softly.assertAll();
}
// endregion
// region helper functions
private String getBase64EncodedGeoShapeQuery(GeoJson<?> geoJson, String elasticFieldName, String relation) {
return Base64.getEncoder()
.encodeToString(("{\"geo_shape\": {\""
+ elasticFieldName + "\": {\"shape\": " + Document
.from(Objects.requireNonNull(GeoConverters.GeoJsonToMapConverter.INSTANCE.convert(geoJson))).toJson()
+ ", \"relation\": \"" + relation + "\"}}}").getBytes());
}
// endregion
// region test entities
static class Person {
@Nullable
@Id String id;
@Nullable
@Field(name = "first-name") String firstName;
@Nullable
@Field(name = "last-name") String lastName;
@Nullable
@MultiField(mainField = @Field(name = "nick-name"),
otherFields = { @InnerField(suffix = "keyword", type = FieldType.Keyword) }) String nickName;
@Nullable
@Field(name = "created-date", type = FieldType.Date, format = DateFormat.epoch_millis) Date createdDate;
@Nullable
@Field(name = "birth-date", type = FieldType.Date, format = {}, pattern = "dd.MM.uuuu") LocalDate birthDate;
}
static class House {
@Nullable
@Id String id;
@Nullable
@Field(name = "per-sons", type = FieldType.Nested) List<Person> persons;
}
static class ObjectWithPerson {
@Nullable
@Id String id;
@Nullable
@Field(name = "per-sons", type = FieldType.Object) List<Person> persons;
}
static class GeoShapeEntity {
@Nullable
@Field(name = "geo-shape-field") GeoJson<?> geoShapeField;
}
// endregion
}

View File

@ -1,476 +0,0 @@
/*
* Copyright 2020-2023 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.erhlc;
import static org.skyscreamer.jsonassert.JSONAssert.*;
import org.json.JSONException;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.data.elasticsearch.core.query.Criteria;
/**
* @author Peter-Josef Meisch
* @author Ezequiel Antúnez Camacho
*/
@SuppressWarnings("ConstantConditions")
class CriteriaQueryProcessorUnitTests {
private final CriteriaQueryProcessor queryProcessor = new CriteriaQueryProcessor();
@Test // DATAES-706
void shouldProcessTwoCriteriaWithAnd() throws JSONException {
String expected = """
{
"bool": {
"must": [
{
"query_string": {
"query": "value1",
"fields": [
"field1^1.0"
]
}
},
{
"query_string": {
"query": "value2",
"fields": [
"field2^1.0"
]
}
}
]
}
}"""; //
Criteria criteria = new Criteria("field1").is("value1").and("field2").is("value2");
String query = queryProcessor.createQuery(criteria).toString();
assertEquals(expected, query, false);
}
@Test // DATAES-706
void shouldProcessTwoCriteriaWithOr() throws JSONException {
String expected = """
{
"bool": {
"should": [
{
"query_string": {
"query": "value1",
"fields": [
"field1^1.0"
]
}
},
{
"query_string": {
"query": "value2",
"fields": [
"field2^1.0"
]
}
}
]
}
}"""; //
Criteria criteria = new Criteria("field1").is("value1").or("field2").is("value2");
String query = queryProcessor.createQuery(criteria).toString();
assertEquals(expected, query, false);
}
@Test // DATAES-706
void shouldProcessMixedCriteriaWithOrAnd() throws JSONException {
String expected = """
{
"bool": {
"must": [
{
"query_string": {
"query": "value1",
"fields": [
"field1^1.0"
]
}
},
{
"query_string": {
"query": "value3",
"fields": [
"field3^1.0"
]
}
}
],
"should": [
{
"query_string": {
"query": "value2",
"fields": [
"field2^1.0"
]
}
},
{
"query_string": {
"query": "value4",
"fields": [
"field4^1.0"
]
}
}
]
}
}
"""; //
Criteria criteria = new Criteria("field1").is("value1") //
.or("field2").is("value2") //
.and("field3").is("value3") //
.or("field4").is("value4"); //
String query = queryProcessor.createQuery(criteria).toString();
assertEquals(expected, query, false);
}
@Test // DATAES-706
void shouldAddSubQuery() throws JSONException {
String expected = """
{
"bool": {
"must": [
{
"query_string": {
"query": "Miller",
"fields": [
"lastName^1.0"
]
}
},
{
"bool": {
"should": [
{
"query_string": {
"query": "John",
"fields": [
"firstName^1.0"
]
}
},
{
"query_string": {
"query": "Jack",
"fields": [
"firstName^1.0"
]
}
}
]
}
}
]
}
}"""; //
Criteria criteria = new Criteria("lastName").is("Miller")
.subCriteria(new Criteria().or("firstName").is("John").or("firstName").is("Jack"));
String query = queryProcessor.createQuery(criteria).toString();
assertEquals(expected, query, false);
}
@Test // DATAES-706
void shouldProcessNestedSubCriteria() throws JSONException {
String expected = """
{
"bool": {
"should": [
{
"bool": {
"must": [
{
"query_string": {
"query": "Miller",
"fields": [
"lastName^1.0"
]
}
},
{
"bool": {
"should": [
{
"query_string": {
"query": "Jack",
"fields": [
"firstName^1.0"
]
}
},
{
"query_string": {
"query": "John",
"fields": [
"firstName^1.0"
]
}
}
]
}
}
]
}
},
{
"bool": {
"must": [
{
"query_string": {
"query": "Smith",
"fields": [
"lastName^1.0"
]
}
},
{
"bool": {
"should": [
{
"query_string": {
"query": "Emma",
"fields": [
"firstName^1.0"
]
}
},
{
"query_string": {
"query": "Lucy",
"fields": [
"firstName^1.0"
]
}
}
]
}
}
]
}
}
]
}
}"""; //
Criteria criteria = Criteria.or()
.subCriteria(new Criteria("lastName").is("Miller")
.subCriteria(new Criteria().or("firstName").is("John").or("firstName").is("Jack")))
.subCriteria(new Criteria("lastName").is("Smith")
.subCriteria(new Criteria().or("firstName").is("Emma").or("firstName").is("Lucy")));
String query = queryProcessor.createQuery(criteria).toString();
assertEquals(expected, query, false);
}
@Test // DATAES-706
void shouldBuildMatchQuery() throws JSONException {
String expected = """
{
"bool" : {
"must" : [
{
"match" : {
"field1" : {
"query" : "value1 value2",
"operator" : "OR"
}
}
}
]
}
}
"""; //
Criteria criteria = new Criteria("field1").matches("value1 value2");
String query = queryProcessor.createQuery(criteria).toString();
assertEquals(expected, query, false);
}
@Test // DATAES-706
void shouldBuildMatchAllQuery() throws JSONException {
String expected = """
{
"bool" : {
"must" : [
{
"match" : {
"field1" : {
"query" : "value1 value2",
"operator" : "AND"
}
}
}
]
}
}
"""; //
Criteria criteria = new Criteria("field1").matchesAll("value1 value2");
String query = queryProcessor.createQuery(criteria).toString();
assertEquals(expected, query, false);
}
@Test // #1753
@DisplayName("should build nested query")
void shouldBuildNestedQuery() throws JSONException {
String expected = """
{
"bool" : {
"must" : [
{
"nested" : {
"query" : {
"query_string" : {
"query" : "murphy",
"fields" : [
"houses.inhabitants.lastName^1.0"
]
}
},
"path" : "houses.inhabitants"
}
}
]
}
}"""; //
Criteria criteria = new Criteria("houses.inhabitants.lastName").is("murphy");
criteria.getField().setPath("houses.inhabitants");
String query = queryProcessor.createQuery(criteria).toString();
assertEquals(expected, query, false);
}
@Test // #1909
@DisplayName("should build query for empty property")
void shouldBuildQueryForEmptyProperty() throws JSONException {
String expected = """
{
"bool" : {
"must" : [
{
"bool" : {
"must" : [
{
"exists" : {
"field" : "lastName" }
}
],
"must_not" : [
{
"wildcard" : {
"lastName" : {
"wildcard" : "*" }
}
}
]
}
}
]
}
}"""; //
Criteria criteria = new Criteria("lastName").empty();
String query = queryProcessor.createQuery(criteria).toString();
assertEquals(expected, query, false);
}
@Test // #1909
@DisplayName("should build query for non-empty property")
void shouldBuildQueryForNonEmptyProperty() throws JSONException {
String expected = """
{
"bool" : {
"must" : [
{
"wildcard" : {
"lastName" : {
"wildcard" : "*"
}
}
}
]
}
}
"""; //
Criteria criteria = new Criteria("lastName").notEmpty();
String query = queryProcessor.createQuery(criteria).toString();
assertEquals(expected, query, false);
}
@Test // #2418
void shouldBuildRegexpQuery() throws JSONException {
String expected = """
{
"bool": {
"must": [
{
"regexp": {
"field1": {
"value": "[^abc]"
}
}
}
]
}
}
""";
Criteria criteria = new Criteria("field1").regexp("[^abc]");
String queryString = queryProcessor.createQuery(criteria).toString();
assertEquals(expected, queryString, false);
}
}

View File

@ -1,114 +0,0 @@
/*
* Copyright 2020-2023 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.erhlc;
import static org.assertj.core.api.Assertions.*;
import static org.elasticsearch.search.internal.SearchContext.*;
import static org.mockito.Mockito.*;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import java.net.URI;
import java.util.Optional;
import java.util.function.Function;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.client.Request;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.FetchSourceContext;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.Spy;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.data.elasticsearch.RestStatusException;
import org.springframework.http.HttpStatus;
import org.springframework.web.reactive.function.client.ClientResponse;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.reactive.function.client.WebClient.ResponseSpec;
import org.springframework.web.util.UriBuilder;
/**
* @author Peter-Josef Meisch
*/
@ExtendWith(MockitoExtension.class)
class DefaultReactiveElasticsearchClientTest {
@Mock private HostProvider hostProvider;
@Mock private Function<SearchRequest, Request> searchRequestConverter;
@Spy private RequestCreator requestCreator;
@Mock private WebClient webClient;
@Test
void shouldSetAppropriateRequestParametersOnCount() {
when(requestCreator.search()).thenReturn(searchRequestConverter);
SearchRequest searchRequest = new SearchRequest("someindex") //
.source(new SearchSourceBuilder().query(QueryBuilders.matchAllQuery()));
ReactiveElasticsearchClient client = new DefaultReactiveElasticsearchClient(hostProvider, requestCreator) {
@Override
public Mono<ResponseSpec> execute(ReactiveElasticsearchClientCallback callback) {
return Mono.empty();
}
};
client.count(searchRequest).as(StepVerifier::create).verifyComplete();
ArgumentCaptor<SearchRequest> captor = ArgumentCaptor.forClass(SearchRequest.class);
verify(searchRequestConverter).apply(captor.capture());
SearchSourceBuilder source = captor.getValue().source();
assertThat(source.size()).isEqualTo(0);
assertThat(source.trackTotalHitsUpTo()).isEqualTo(TRACK_TOTAL_HITS_ACCURATE);
assertThat(source.fetchSource()).isEqualTo(FetchSourceContext.DO_NOT_FETCH_SOURCE);
}
@Test // #1712
@DisplayName("should throw RestStatusException on server 5xx with empty body")
void shouldThrowRestStatusExceptionOnServer5xxWithEmptyBody() {
when(hostProvider.getActive(any())).thenReturn(Mono.just(webClient));
WebClient.RequestBodyUriSpec requestBodyUriSpec = mock(WebClient.RequestBodyUriSpec.class);
when(requestBodyUriSpec.uri((Function<UriBuilder, URI>) any())).thenReturn(requestBodyUriSpec);
when(requestBodyUriSpec.attribute(any(), any())).thenReturn(requestBodyUriSpec);
when(requestBodyUriSpec.headers(any())).thenReturn(requestBodyUriSpec);
when(webClient.method(any())).thenReturn(requestBodyUriSpec);
when(requestBodyUriSpec.exchangeToMono(any())).thenAnswer(invocationOnMock -> {
Function<ClientResponse, ? extends Mono<?>> responseHandler = invocationOnMock.getArgument(0);
ClientResponse clientResponse = mock(ClientResponse.class);
when(clientResponse.statusCode()).thenReturn(HttpStatus.SERVICE_UNAVAILABLE);
ClientResponse.Headers headers = mock(ClientResponse.Headers.class);
when(headers.contentType()).thenReturn(Optional.empty());
when(clientResponse.headers()).thenReturn(headers);
when(clientResponse.body(any())).thenReturn(Mono.empty());
return responseHandler.apply(clientResponse);
});
ReactiveElasticsearchClient client = new DefaultReactiveElasticsearchClient(hostProvider, requestCreator);
client.get(new GetRequest("42")) //
.as(StepVerifier::create) //
.expectError(RestStatusException.class) //
.verify(); //
}
}

View File

@ -1,60 +0,0 @@
/*
* Copyright 2018-2023 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.erhlc;
import static org.assertj.core.api.Assertions.*;
import java.net.InetSocketAddress;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;
import org.junit.jupiter.api.Test;
import org.springframework.web.reactive.function.client.WebClient;
/**
* @author Christoph Strobl
* @author Peter-Josef Meisch
*/
public class DefaultWebClientProviderUnitTests {
@Test // DATAES-488
public void shouldCacheClients() {
DefaultWebClientProvider provider = new DefaultWebClientProvider("http", null);
WebClient client1 = provider.get(InetSocketAddress.createUnresolved("localhost", 9200));
WebClient shouldBeCachedInstanceOfClient1 = provider.get(InetSocketAddress.createUnresolved("localhost", 9200));
WebClient notClient1ButAnotherInstance = provider.get(InetSocketAddress.createUnresolved("127.0.0.1", 9200));
assertThat(shouldBeCachedInstanceOfClient1).isSameAs(client1);
assertThat(notClient1ButAnotherInstance).isNotSameAs(client1);
}
@Test // DATAES-719
void shouldCallWebClientConfigurer() {
AtomicReference<Boolean> configurerCalled = new AtomicReference<>(false);
Function<WebClient, WebClient> configurer = webClient -> {
configurerCalled.set(true);
return webClient;
};
WebClientProvider provider = new DefaultWebClientProvider("http", null).withWebClientConfigurer(configurer);
provider.get(InetSocketAddress.createUnresolved("localhost", 9200));
assertThat(configurerCalled).hasValue(true);
}
}

View File

@ -1,297 +0,0 @@
/*
* Copyright 2014-2023 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.erhlc;
import static org.assertj.core.api.Assertions.*;
import static org.elasticsearch.index.query.QueryBuilders.*;
import static org.skyscreamer.jsonassert.JSONAssert.*;
import java.time.Duration;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.elasticsearch.action.support.ActiveShardCount;
import org.elasticsearch.action.support.WriteRequest;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.common.lucene.search.function.CombineFunction;
import org.elasticsearch.common.lucene.search.function.FunctionScoreQuery;
import org.elasticsearch.core.TimeValue;
import org.elasticsearch.index.query.InnerHitBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
import org.elasticsearch.index.query.functionscore.GaussDecayFunctionBuilder;
import org.elasticsearch.index.reindex.UpdateByQueryRequest;
import org.elasticsearch.join.query.ParentIdQueryBuilder;
import org.elasticsearch.script.Script;
import org.elasticsearch.search.collapse.CollapseBuilder;
import org.elasticsearch.search.fetch.subphase.FetchSourceContext;
import org.json.JSONException;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.elasticsearch.core.ElasticsearchIntegrationTests;
import org.springframework.data.elasticsearch.core.RefreshPolicy;
import org.springframework.data.elasticsearch.core.ScriptType;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.BaseQueryBuilder;
import org.springframework.data.elasticsearch.core.query.FetchSourceFilterBuilder;
import org.springframework.data.elasticsearch.core.query.IndicesOptions;
import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.data.elasticsearch.core.query.RescorerQuery;
import org.springframework.data.elasticsearch.core.query.UpdateQuery;
import org.springframework.data.elasticsearch.junit.jupiter.ElasticsearchRestTemplateConfiguration;
import org.springframework.data.elasticsearch.utils.IndexNameProvider;
import org.springframework.lang.Nullable;
import org.springframework.test.context.ContextConfiguration;
/**
* @author Rizwan Idrees
* @author Mohsin Husen
* @author Franck Marchand
* @author Abdul Mohammed
* @author Kevin Leturc
* @author Mason Chan
* @author Chris White
* @author Ilkang Na
* @author Alen Turkovic
* @author Sascha Woo
* @author Don Wellington
* @author Peter-Josef Meisch
* @author Farid Faoudi
*/
@ContextConfiguration(classes = { ElasticsearchERHLCIntegrationTests.Config.class })
@DisplayName("Using Elasticsearch RestHighLevelClient")
public class ElasticsearchERHLCIntegrationTests extends ElasticsearchIntegrationTests {
@Configuration
@Import({ ElasticsearchRestTemplateConfiguration.class })
static class Config {
@Bean
IndexNameProvider indexNameProvider() {
return new IndexNameProvider("integration-es7");
}
}
@Override
protected Query queryWithIds(String... ids) {
return new NativeSearchQueryBuilder().withIds(ids).build();
}
@Override
protected Query getTermQuery(String field, String value) {
return new NativeSearchQueryBuilder().withQuery(termQuery(field, value)).build();
}
@Override
protected BaseQueryBuilder<?, ?> getBuilderWithTermQuery(String field, String value) {
return new NativeSearchQueryBuilder().withQuery(termQuery(field, value));
}
@Override
protected BaseQueryBuilder<?, ?> getBuilderWithMatchQuery(String field, String value) {
return new NativeSearchQueryBuilder().withQuery(matchQuery(field, value));
}
@Override
protected BaseQueryBuilder<?, ?> getBuilderWithWildcardQuery(String field, String value) {
return new NativeSearchQueryBuilder().withQuery(wildcardQuery(field, value));
}
@Override
protected BaseQueryBuilder<?, ?> getBuilderWithMatchAllQuery() {
return new NativeSearchQueryBuilder().withQuery(matchAllQuery());
}
@Override
protected Query getBoolQueryWithWildcardsFirstMustSecondShouldAndMinScore(String firstField, String firstValue,
String secondField, String secondValue, float minScore) {
return new NativeSearchQueryBuilder().withQuery( //
boolQuery() //
.must(wildcardQuery(firstField, firstValue)) //
.should(wildcardQuery(secondField, secondValue))) //
.withMinScore(minScore).build();
}
@Override
protected Query getQueryWithCollapse(String collapseField, @Nullable String innerHits, @Nullable Integer size) {
CollapseBuilder collapseBuilder = new CollapseBuilder(collapseField);
if (innerHits != null) {
InnerHitBuilder innerHitBuilder = new InnerHitBuilder(innerHits);
if (size != null) {
innerHitBuilder.setSize(size);
}
collapseBuilder.setInnerHits(innerHitBuilder);
}
return new NativeSearchQueryBuilder().withQuery(matchAllQuery()).withCollapseBuilder(collapseBuilder).build();
}
@Override
protected Query getMatchAllQueryWithFilterForId(String id) {
return new NativeSearchQueryBuilder().withQuery(matchAllQuery()).withFilter(boolQuery().filter(termQuery("id", id)))
.build();
}
@Override
protected Query getQueryForParentId(String type, String id, @Nullable String route) {
NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder()
.withQuery(new ParentIdQueryBuilder(type, id));
if (route != null) {
queryBuilder.withRoute(route);
}
return queryBuilder.build();
}
@Override
protected Query getMatchAllQueryWithIncludesAndInlineExpressionScript(@Nullable String includes, String fieldName,
String script, Map<String, java.lang.Object> params) {
NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder().withQuery(matchAllQuery());
if (includes != null) {
nativeSearchQueryBuilder.withSourceFilter(new FetchSourceFilterBuilder().withIncludes(includes).build());
}
return nativeSearchQueryBuilder.withScriptField(new ScriptField(fieldName,
new Script(org.elasticsearch.script.ScriptType.INLINE, "expression", script, params))).build();
}
@Override
protected Query getQueryWithRescorer() {
return new NativeSearchQueryBuilder() //
.withQuery( //
boolQuery() //
.filter(existsQuery("rate")) //
.should(termQuery("message", "message"))) //
.withRescorerQuery( //
new RescorerQuery( //
new NativeSearchQueryBuilder() //
.withQuery(QueryBuilders
.functionScoreQuery(new FunctionScoreQueryBuilder.FilterFunctionBuilder[] {
new FunctionScoreQueryBuilder.FilterFunctionBuilder(
new GaussDecayFunctionBuilder("rate", 0, 10, null, 0.5).setWeight(1f)),
new FunctionScoreQueryBuilder.FilterFunctionBuilder(
new GaussDecayFunctionBuilder("rate", 0, 10, null, 0.5).setWeight(100f)) }) //
.scoreMode(FunctionScoreQuery.ScoreMode.SUM) //
.maxBoost(80f) //
.boostMode(CombineFunction.REPLACE)) //
.build())//
.withScoreMode(RescorerQuery.ScoreMode.Max) //
.withWindowSize(100)) //
.build();
}
@Test // DATAES-768
void shouldUseAllOptionsFromUpdateQuery() {
Map<String, Object> doc = new HashMap<>();
doc.put("id", "1");
doc.put("message", "test");
org.springframework.data.elasticsearch.core.document.Document document = org.springframework.data.elasticsearch.core.document.Document
.from(doc);
UpdateQuery updateQuery = UpdateQuery.builder("1") //
.withDocument(document) //
.withIfSeqNo(42) //
.withIfPrimaryTerm(13) //
.withScript("script")//
.withLang("lang") //
.withRefreshPolicy(RefreshPolicy.WAIT_UNTIL) //
.withRetryOnConflict(7) //
.withTimeout("4711s") //
.withWaitForActiveShards("all") //
.withFetchSourceIncludes(Collections.singletonList("incl")) //
.withFetchSourceExcludes(Collections.singletonList("excl")) //
.build();
UpdateRequest request = getRequestFactory().updateRequest(updateQuery, IndexCoordinates.of("index"));
assertThat(request).isNotNull();
assertThat(request.ifSeqNo()).isEqualTo(42);
assertThat(request.ifPrimaryTerm()).isEqualTo(13);
assertThat(request.script().getIdOrCode()).isEqualTo("script");
assertThat(request.script().getLang()).isEqualTo("lang");
assertThat(request.getRefreshPolicy()).isEqualByComparingTo(WriteRequest.RefreshPolicy.WAIT_UNTIL);
assertThat(request.retryOnConflict()).isEqualTo(7);
assertThat(request.timeout()).isEqualByComparingTo(TimeValue.parseTimeValue("4711s", "test"));
assertThat(request.waitForActiveShards()).isEqualTo(ActiveShardCount.ALL);
FetchSourceContext fetchSourceContext = request.fetchSource();
assertThat(fetchSourceContext).isNotNull();
assertThat(fetchSourceContext.includes()).containsExactlyInAnyOrder("incl");
assertThat(fetchSourceContext.excludes()).containsExactlyInAnyOrder("excl");
}
@Test // #1446, #2191
void shouldUseAllOptionsFromUpdateByQuery() throws JSONException {
Query searchQuery = new NativeSearchQueryBuilder().withQuery(matchAllQuery()) //
.withIndicesOptions(IndicesOptions.LENIENT_EXPAND_OPEN) //
.build(); //
searchQuery.setScrollTime(Duration.ofMillis(1000));
UpdateQuery updateQuery = UpdateQuery.builder(searchQuery) //
.withAbortOnVersionConflict(true) //
.withBatchSize(10) //
.withMaxDocs(12) //
.withMaxRetries(3) //
.withPipeline("pipeline") //
.withRequestsPerSecond(5F) //
.withShouldStoreResult(false) //
.withSlices(4) //
.withScriptType(ScriptType.INLINE) //
.withScript("script") //
.withLang("painless") //
.build(); //
String expectedSearchRequest = '{' + //
" \"size\": 10," + //
" \"query\": {" + //
" \"match_all\": {" + //
" \"boost\": 1.0" + //
" }" + " }" + '}';
// when
UpdateByQueryRequest request = getRequestFactory().updateByQueryRequest(updateQuery, IndexCoordinates.of("index1", "index2"));
// then
assertThat(request.indices()).containsExactlyInAnyOrder("index1", "index2");
assertThat(request).isNotNull();
assertThat(request.getSearchRequest().indicesOptions()).usingRecursiveComparison()
.isEqualTo(IndicesOptions.LENIENT_EXPAND_OPEN);
assertThat(request.getScrollTime().getMillis()).isEqualTo(1000);
assertEquals(request.getSearchRequest().source().toString(), expectedSearchRequest, false);
assertThat(request.isAbortOnVersionConflict()).isTrue();
assertThat(request.getBatchSize()).isEqualTo(10);
assertThat(request.getMaxDocs()).isEqualTo(12);
assertThat(request.getPipeline()).isEqualTo("pipeline");
assertThat(request.getRequestsPerSecond()).isEqualTo(5F);
assertThat(request.getShouldStoreResult()).isFalse();
assertThat(request.getSlices()).isEqualTo(4);
assertThat(request.getScript().getIdOrCode()).isEqualTo("script");
assertThat(request.getScript().getType()).isEqualTo(org.elasticsearch.script.ScriptType.INLINE);
assertThat(request.getScript().getLang()).isEqualTo("painless");
}
private RequestFactory getRequestFactory() {
return ((ElasticsearchRestTemplate) operations).getRequestFactory();
}
}

View File

@ -1,61 +0,0 @@
/*
* Copyright 2020-2023 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.erhlc;
import static org.assertj.core.api.Assertions.*;
import org.elasticsearch.ElasticsearchStatusException;
import org.elasticsearch.index.engine.VersionConflictEngineException;
import org.elasticsearch.index.shard.ShardId;
import org.elasticsearch.rest.RestStatus;
import org.junit.jupiter.api.Test;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.OptimisticLockingFailureException;
/**
* @author Roman Puchkovskiy
* @author Peter-Josef Meisch
*/
class ElasticsearchExceptionTranslatorTests {
private final ElasticsearchExceptionTranslator translator = new ElasticsearchExceptionTranslator();
@Test // DATAES-799
void shouldConvertElasticsearchStatusExceptionWithSeqNoConflictToOptimisticLockingFailureException() {
ElasticsearchStatusException ex = new ElasticsearchStatusException(
"Elasticsearch exception [type=version_conflict_engine_exception, reason=[WPUUsXEB6uuA6j8_A7AB]: version conflict, required seqNo [34], primary term [16]. current document has seqNo [35] and primary term [16]]",
RestStatus.CONFLICT);
DataAccessException translated = translator.translateExceptionIfPossible(ex);
assertThat(translated).isInstanceOf(OptimisticLockingFailureException.class);
assertThat(translated.getMessage()).startsWith("Cannot index a document due to seq_no+primary_term conflict");
assertThat(translated.getCause()).isSameAs(ex);
}
@Test // DATAES-799
void shouldConvertVersionConflictEngineExceptionWithSeqNoConflictToOptimisticLockingFailureException() {
VersionConflictEngineException ex = new VersionConflictEngineException(new ShardId("index", "uuid", 1),
"exception-id",
"Elasticsearch exception [type=version_conflict_engine_exception, reason=[WPUUsXEB6uuA6j8_A7AB]: version conflict, required seqNo [34], primary term [16]. current document has seqNo [35] and primary term [16]]");
DataAccessException translated = translator.translateExceptionIfPossible(ex);
assertThat(translated).isInstanceOf(OptimisticLockingFailureException.class);
assertThat(translated.getMessage()).startsWith("Cannot index a document due to seq_no+primary_term conflict");
assertThat(translated.getCause()).isSameAs(ex);
}
}

View File

@ -1,47 +0,0 @@
/*
* Copyright 2022-2023 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.erhlc;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.ElasticsearchPartQueryIntegrationTests;
import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.data.elasticsearch.junit.jupiter.ElasticsearchRestTemplateConfiguration;
import org.springframework.test.context.ContextConfiguration;
/**
* The base class for these tests lives in the org.springframework.data.elasticsearch.core.query package, but we need
* access to the {@link RequestFactory} class here
*
* @author Peter-Josef Meisch
* @since 4.4
*/
@ContextConfiguration(classes = { ElasticsearchPartQueryERHLCIntegrationTests.Config.class })
public class ElasticsearchPartQueryERHLCIntegrationTests extends ElasticsearchPartQueryIntegrationTests {
@Configuration
@Import({ ElasticsearchRestTemplateConfiguration.class })
static class Config {}
protected String buildQueryString(Query query, Class<?> clazz) {
SearchSourceBuilder source = new RequestFactory(operations.getElasticsearchConverter())
.searchRequest(query, null, clazz, IndexCoordinates.of("dummy")).source();
// remove defaultboost values
return source.toString().replaceAll("(\\^\\d+\\.\\d+)", "");
}
}

View File

@ -1,159 +0,0 @@
/*
* Copyright 2018-2023 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.erhlc;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.Mockito.*;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import java.util.function.Function;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.invocation.InvocationOnMock;
import org.springframework.data.elasticsearch.client.ElasticsearchHost;
import org.springframework.data.elasticsearch.client.ElasticsearchHost.State;
import org.springframework.data.elasticsearch.client.erhlc.HostProvider.Verification;
import org.springframework.data.elasticsearch.client.erhlc.ReactiveMockClientTestsUtils.MockDelegatingElasticsearchHostProvider;
import org.springframework.data.elasticsearch.client.erhlc.ReactiveMockClientTestsUtils.MockWebClientProvider.Receive;
import org.springframework.web.reactive.function.client.ClientResponse;
/**
* @author Christoph Strobl
* @author Peter-Josef Meisch
*/
public class MultiNodeHostProviderUnitTests {
static final String HOST_1 = ":9200";
static final String HOST_2 = ":9201";
static final String HOST_3 = ":9202";
MockDelegatingElasticsearchHostProvider<MultiNodeHostProvider> multiNodeDelegatingHostProvider;
MultiNodeHostProvider delegateHostProvider;
@BeforeEach
public void setUp() {
multiNodeDelegatingHostProvider = ReactiveMockClientTestsUtils.multi(HOST_1, HOST_2, HOST_3);
delegateHostProvider = multiNodeDelegatingHostProvider.getDelegate();
}
@Test // DATAES-488
public void refreshHostStateShouldUpdateNodeStateCorrectly() {
multiNodeDelegatingHostProvider.when(HOST_1).receive(Receive::error);
multiNodeDelegatingHostProvider.when(HOST_2).receive(Receive::ok);
multiNodeDelegatingHostProvider.when(HOST_3).receive(Receive::ok);
delegateHostProvider.clusterInfo().as(StepVerifier::create).expectNextCount(1).verifyComplete();
assertThat(delegateHostProvider.getCachedHostState()).extracting(ElasticsearchHost::getState)
.containsExactly(State.OFFLINE, State.ONLINE, State.ONLINE);
}
@Test // DATAES-488
public void getActiveReturnsFirstActiveHost() {
multiNodeDelegatingHostProvider.when(HOST_1).receive(Receive::error);
multiNodeDelegatingHostProvider.when(HOST_2).receive(Receive::ok);
multiNodeDelegatingHostProvider.when(HOST_3).receive(Receive::error);
delegateHostProvider.getActive().as(StepVerifier::create).expectNext(multiNodeDelegatingHostProvider.client(HOST_2))
.verifyComplete();
}
@Test // DATAES-488
public void getActiveErrorsWhenNoActiveHostFound() {
multiNodeDelegatingHostProvider.when(HOST_1).receive(Receive::error);
multiNodeDelegatingHostProvider.when(HOST_2).receive(Receive::error);
multiNodeDelegatingHostProvider.when(HOST_3).receive(Receive::error);
delegateHostProvider.getActive().as(StepVerifier::create).expectError(IllegalStateException.class);
}
@Test // DATAES-488
public void lazyModeDoesNotResolveHostsTwice() {
multiNodeDelegatingHostProvider.when(HOST_1).receive(Receive::error);
multiNodeDelegatingHostProvider.when(HOST_2).receive(Receive::ok);
multiNodeDelegatingHostProvider.when(HOST_3).receive(Receive::error);
delegateHostProvider.clusterInfo().as(StepVerifier::create).expectNextCount(1).verifyComplete();
delegateHostProvider.getActive(Verification.LAZY).as(StepVerifier::create)
.expectNext(multiNodeDelegatingHostProvider.client(HOST_2)).verifyComplete();
verify(multiNodeDelegatingHostProvider.client(":9201")).head();
}
@Test // DATAES-488
public void alwaysModeDoesNotResolveHostsTwice() {
multiNodeDelegatingHostProvider.when(HOST_1).receive(Receive::error);
multiNodeDelegatingHostProvider.when(HOST_2).receive(Receive::ok);
multiNodeDelegatingHostProvider.when(HOST_3).receive(Receive::error);
delegateHostProvider.clusterInfo().as(StepVerifier::create).expectNextCount(1).verifyComplete();
delegateHostProvider.getActive(Verification.ACTIVE).as(StepVerifier::create)
.expectNext(multiNodeDelegatingHostProvider.client(HOST_2)).verifyComplete();
verify(multiNodeDelegatingHostProvider.client(HOST_2), times(2)).head();
}
@Test // DATAES-488
public void triesDeadHostsIfNoActiveFound() {
multiNodeDelegatingHostProvider.when(HOST_1).receive(Receive::error);
multiNodeDelegatingHostProvider.when(HOST_2).get(requestHeadersUriSpec -> {
ClientResponse response1 = mock(ClientResponse.class);
when(response1.releaseBody()).thenReturn(Mono.empty());
Receive.error(response1);
ClientResponse response2 = mock(ClientResponse.class);
when(response2.releaseBody()).thenReturn(Mono.empty());
Receive.ok(response2);
when(requestHeadersUriSpec.exchangeToMono(any()))//
.thenAnswer(invocation -> getAnswer(invocation, response1)) //
.thenAnswer(invocation -> getAnswer(invocation, response2));
});
multiNodeDelegatingHostProvider.when(HOST_3).receive(Receive::error);
delegateHostProvider.clusterInfo().as(StepVerifier::create).expectNextCount(1).verifyComplete();
assertThat(delegateHostProvider.getCachedHostState()).extracting(ElasticsearchHost::getState)
.containsExactly(State.OFFLINE, State.OFFLINE, State.OFFLINE);
delegateHostProvider.getActive().as(StepVerifier::create).expectNext(multiNodeDelegatingHostProvider.client(HOST_2))
.verifyComplete();
verify(multiNodeDelegatingHostProvider.client(HOST_2), times(2)).head();
}
private Mono<?> getAnswer(InvocationOnMock invocation, ClientResponse response) {
final Function<ClientResponse, ? extends Mono<?>> responseHandler = invocation.getArgument(0);
if (responseHandler != null) {
return responseHandler.apply(response);
}
return Mono.empty();
}
}

View File

@ -1,731 +0,0 @@
/*
* Copyright 2018-2023 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.erhlc;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.Mockito.*;
import static org.springframework.data.elasticsearch.client.erhlc.ReactiveMockClientTestsUtils.MockWebClientProvider.Receive.*;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import java.io.IOException;
import java.net.URI;
import java.time.Instant;
import java.util.Collections;
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;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.core.TimeValue;
import org.elasticsearch.index.VersionType;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.metrics.ParsedMax;
import org.elasticsearch.xcontent.XContentType;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.reactivestreams.Publisher;
import org.springframework.data.elasticsearch.RestStatusException;
import org.springframework.data.elasticsearch.client.erhlc.ReactiveMockClientTestsUtils.MockDelegatingElasticsearchHostProvider;
import org.springframework.data.elasticsearch.client.erhlc.ReactiveMockClientTestsUtils.MockWebClientProvider.Receive;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.util.StreamUtils;
/**
* @author Christoph Strobl
* @author Henrique Amaral
* @author Russell Parry
*/
public class ReactiveElasticsearchClientUnitTests {
static final String HOST = ":9200";
MockDelegatingElasticsearchHostProvider<? extends HostProvider<?>> hostProvider;
ReactiveElasticsearchClient client;
@BeforeEach
public void setUp() {
hostProvider = ReactiveMockClientTestsUtils.provider(HOST).withActiveDefaultHost(HOST);
client = new DefaultReactiveElasticsearchClient(hostProvider);
}
@Test // DATAES-512
public void sendRequestShouldCarryOnRequestParameters() {
hostProvider.when(HOST).receiveDeleteOk();
DeleteRequest request = new DeleteRequest("index", "type", "id");
request.version(1000);
request.versionType(VersionType.EXTERNAL);
request.timeout(TimeValue.timeValueMinutes(10));
client.delete(request) //
.then() //
.as(StepVerifier::create) //
.verifyComplete();
URI uri = hostProvider.when(HOST).captureUri();
assertThat(uri.getQuery()) //
.contains("version=1000") //
.contains("version_type=external") //
.contains("timeout=10m");
}
// --> PING
@Test
public void pingShouldHitMainEndpoint() {
hostProvider.when(HOST) //
.receive(Receive::ok);
client.ping() //
.then() //
.as(StepVerifier::create) //
.verifyComplete();
URI uri = hostProvider.when(HOST).captureUri();
assertThat(uri.getRawPath()).isEqualTo("/");
}
@Test // DATAES-488
public void pingShouldReturnTrueOnHttp200() {
hostProvider.when(HOST) //
.receive(Receive::ok);
client.ping() //
.as(StepVerifier::create) //
.expectNext(true) //
.verifyComplete();
}
@Test // DATAES-488
public void pingShouldReturnFalseOnNonHttp200() {
hostProvider.when(HOST) //
.receive(Receive::error);
client.ping() //
.as(StepVerifier::create) //
.expectNext(false) //
.verifyComplete();
}
// --> INFO
@Test
public void infoShouldHitMainEndpoint() {
hostProvider.when(HOST) //
.receiveInfo();
client.info() //
.then() //
.as(StepVerifier::create) //
.verifyComplete();
URI uri = hostProvider.when(HOST).captureUri();
assertThat(uri.getRawPath()).isEqualTo("/");
}
@Test // DATAES-488
public void infoShouldReturnResponseCorrectly() {
hostProvider.when(HOST) //
.receiveInfo();
client.info() //
.as(StepVerifier::create) //
.consumeNextWith(mainResponse -> {}) //
.verifyComplete();
}
// --> GET
@Test // DATAES-488
public void getShouldHitGetEndpoint() {
hostProvider.when(HOST).receive(
clientResponse -> when(clientResponse.statusCode()).thenReturn(HttpStatus.ACCEPTED, HttpStatus.NOT_FOUND));
hostProvider.when(HOST) //
.receiveGetByIdNotFound();
client.get(new GetRequest("twitter").id("1")) //
.then() //
.as(StepVerifier::create) //
.verifyComplete();
verify(hostProvider.client(HOST)).method(HttpMethod.GET);
URI uri = hostProvider.when(HOST).captureUri();
assertThat(uri.getRawPath()).isEqualTo("/twitter/_doc/1");
}
@Test // DATAES-488
public void getShouldReturnExistingDocument() {
hostProvider.when(HOST) //
.receiveGetById();
client.get(new GetRequest("twitter").id("1")) //
.as(StepVerifier::create) //
.consumeNextWith(result -> {
assertThat(result.isExists()).isTrue();
assertThat(result.getIndex()).isEqualTo("twitter");
assertThat(result.getId()).isEqualTo("1");
assertThat(result.getSource()) //
.containsEntry("user", "kimchy") //
.containsEntry("message", "Trying out Elasticsearch, so far so good?") //
.containsKey("post_date");
}) //
.verifyComplete();
}
@Test // DATAES-488
public void getShouldReturnEmptyForNonExisting() {
hostProvider.when(HOST) //
.receiveGetByIdNotFound();
client.get(new GetRequest("twitter").id("1")) //
.as(StepVerifier::create) //
.verifyComplete();
}
// --> MGET
@Test // DATAES-488
public void multiGetShouldHitMGetEndpoint() {
hostProvider.when(HOST) //
.receiveJsonFromFile("multi-get-ok-2-hits");
client.multiGet(new MultiGetRequest().add("twitter", "_doc", "1").add("twitter", "_doc", "2")) //
.then() //
.as(StepVerifier::create) //
.verifyComplete();
verify(hostProvider.client(HOST)).method(HttpMethod.POST);
hostProvider.when(HOST)
.exchange(requestBodyUriSpec -> verify(requestBodyUriSpec).body(any(Publisher.class), any(Class.class)));
URI uri = hostProvider.when(HOST).captureUri();
assertThat(uri.getRawPath()).isEqualTo("/_mget");
}
@Test // DATAES-488, #1678
public void multiGetShouldReturnExistingDocuments() {
hostProvider.when(HOST) //
.receiveJsonFromFile("multi-get-ok-2-hits");
client.multiGet(new MultiGetRequest().add("twitter", "1").add("twitter", "2")) //
.as(StepVerifier::create) //
.consumeNextWith(result -> {
assertThat(result.isFailed()).isFalse();
assertThat(result.getIndex()).isEqualTo("twitter");
assertThat(result.getId()).isEqualTo("1");
assertThat(result.getResponse().getSource()) //
.containsEntry("user", "kimchy") //
.containsEntry("message", "Trying out Elasticsearch, so far so good?") //
.containsKey("post_date");
}) //
.consumeNextWith(result -> {
assertThat(result.isFailed()).isFalse();
assertThat(result.getIndex()).isEqualTo("twitter");
assertThat(result.getId()).isEqualTo("2");
assertThat(result.getResponse().getSource()) //
.containsEntry("user", "kimchy") //
.containsEntry("message", "Another tweet, will it be indexed?") //
.containsKey("post_date");
}) //
.verifyComplete();
}
@Test // DATAES-488, #1678
public void multiGetShouldWorkForNonExistingDocuments() {
hostProvider.when(HOST) //
.receiveJsonFromFile("multi-get-ok-2-hits-1-unavailable");
client.multiGet(new MultiGetRequest() //
.add("twitter", "1") //
.add("twitter", "2") //
.add("twitter", "3") //
) //
.as(StepVerifier::create) //
.consumeNextWith(result -> {
assertThat(result.isFailed()).isFalse();
assertThat(result.getIndex()).isEqualTo("twitter");
assertThat(result.getId()).isEqualTo("1");
assertThat(result.getResponse().isExists()).isTrue();
assertThat(result.getResponse().getSource()) //
.containsEntry("user", "kimchy") //
.containsEntry("message", "Trying out Elasticsearch, so far so good?") //
.containsKey("post_date");
}) //
.consumeNextWith(result -> {
assertThat(result.isFailed()).isFalse();
assertThat(result.getResponse().isExists()).isFalse();
}) //
.consumeNextWith(result -> {
assertThat(result.isFailed()).isFalse();
assertThat(result.getIndex()).isEqualTo("twitter");
assertThat(result.getId()).isEqualTo("3");
assertThat(result.getResponse().isExists()).isTrue();
assertThat(result.getResponse().getSource()) //
.containsEntry("user", "elastic") //
.containsEntry("message", "Building the site, should be kewl") //
.containsKey("post_date");
}) //
.verifyComplete();
}
// --> EXISTS
@Test // DATAES-488
public void existsShouldHitGetEndpoint() {
hostProvider.when(HOST) //
.receiveGetById();
client.exists(new GetRequest("twitter").id("1")) //
.then() //
.as(StepVerifier::create) //
.verifyComplete();
verify(hostProvider.client(HOST)).method(HttpMethod.HEAD);
URI uri = hostProvider.when(HOST).captureUri();
assertThat(uri.getRawPath()).isEqualTo("/twitter/_doc/1");
}
@Test // DATAES-488
public void existsShouldReturnTrueIfExists() {
hostProvider.when(HOST) //
.receiveGetById();
client.exists(new GetRequest("twitter").id("1")) //
.as(StepVerifier::create) //
.expectNext(true).verifyComplete();
}
@Test // DATAES-488
public void existsShouldReturnFalseIfNotExists() {
hostProvider.when(HOST) //
.receiveGetByIdNotFound();
client.exists(new GetRequest("twitter").id("1")) //
.as(StepVerifier::create) //
.expectNext(false).verifyComplete();
}
// --> INDEX
@Test // DATAES-488
public void indexNewShouldHitCreateEndpoint() {
hostProvider.when(HOST) //
.receiveIndexCreated();
client.index(new IndexRequest("twitter").id("10").create(true).source(" { foo : \"bar\" }", XContentType.JSON))
.then() //
.as(StepVerifier::create) //
.verifyComplete();
verify(hostProvider.client(HOST)).method(HttpMethod.PUT);
hostProvider.when(HOST)
.exchange(requestBodyUriSpec -> verify(requestBodyUriSpec).contentType(MediaType.APPLICATION_JSON));
URI uri = hostProvider.when(HOST).captureUri();
assertThat(uri.getRawPath()).isEqualTo("/twitter/_create/10");
}
@Test // DATAES-488
public void indexExistingShouldHitEndpointCorrectly() {
hostProvider.when(HOST) //
.receiveIndexUpdated();
client.index(new IndexRequest("twitter").id("10").source(" { foo : \"bar\" }", XContentType.JSON)).then() //
.as(StepVerifier::create) //
.verifyComplete();
verify(hostProvider.client(HOST)).method(HttpMethod.PUT);
hostProvider.when(HOST)
.exchange(requestBodyUriSpec -> verify(requestBodyUriSpec).contentType(MediaType.APPLICATION_JSON));
URI uri = hostProvider.when(HOST).captureUri();
assertThat(uri.getRawPath()).isEqualTo("/twitter/_doc/10");
}
@Test // DATAES-488
public void indexShouldReturnCreatedWhenNewDocumentIndexed() {
hostProvider.when(HOST) //
.receiveIndexCreated();
client.index(new IndexRequest("twitter").id("10").create(true).source(" { foo : \"bar\" }", XContentType.JSON))
.as(StepVerifier::create) //
.consumeNextWith(response -> {
assertThat(response.getId()).isEqualTo("10");
assertThat(response.getIndex()).isEqualTo("twitter");
assertThat(response.getResult()).isEqualTo(Result.CREATED);
}) //
.verifyComplete();
}
@Test // DATAES-488
public void indexShouldReturnUpdatedWhenExistingDocumentIndexed() {
hostProvider.when(HOST) //
.receiveIndexUpdated();
client.index(new IndexRequest("twitter").id("1").source(" { foo : \"bar\" }", XContentType.JSON))
.as(StepVerifier::create) //
.consumeNextWith(response -> {
assertThat(response.getId()).isEqualTo("1");
assertThat(response.getIndex()).isEqualTo("twitter");
assertThat(response.getResult()).isEqualTo(Result.UPDATED);
}) //
.verifyComplete();
}
// --> UPDATE
@Test // DATAES-488
public void updateShouldHitEndpointCorrectly() {
hostProvider.when(HOST) //
.receiveUpdateOk();
client.update(new UpdateRequest("twitter", "1").doc(Collections.singletonMap("user", "cstrobl"))).then() //
.as(StepVerifier::create) //
.verifyComplete();
verify(hostProvider.client(HOST)).method(HttpMethod.POST);
hostProvider.when(HOST)
.exchange(requestBodyUriSpec -> verify(requestBodyUriSpec).contentType(MediaType.APPLICATION_JSON));
URI uri = hostProvider.when(HOST).captureUri();
assertThat(uri.getRawPath()).isEqualTo("/twitter/_update/1");
}
@Test // DATAES-488
public void updateShouldEmitResponseCorrectly() {
hostProvider.when(HOST) //
.receiveUpdateOk();
client.update(new UpdateRequest("twitter", "doc", "1").doc(Collections.singletonMap("user", "cstrobl")))
.as(StepVerifier::create) //
.consumeNextWith(updateResponse -> {
assertThat(updateResponse.getResult()).isEqualTo(Result.UPDATED);
assertThat(updateResponse.getVersion()).isEqualTo(2);
assertThat(updateResponse.getId()).isEqualTo("1");
assertThat(updateResponse.getIndex()).isEqualTo("twitter");
}) //
.verifyComplete();
}
@Test // DATAES-488, DATAES-767, DATAES-822
public void updateShouldEmitErrorWhenNotFound() {
hostProvider.when(HOST) //
.updateFail();
client.update(new UpdateRequest("twitter", "1").doc(Collections.singletonMap("user", "cstrobl")))
.as(StepVerifier::create) //
.expectError(RestStatusException.class) //
.verify();
}
// --> DELETE
@Test // DATAES-488
public void deleteShouldHitEndpointCorrectly() {
hostProvider.when(HOST) //
.receiveDeleteOk();
client.delete(new DeleteRequest("twitter", "doc", "1")).then() //
.as(StepVerifier::create) //
.verifyComplete();
verify(hostProvider.client(HOST)).method(HttpMethod.DELETE);
URI uri = hostProvider.when(HOST).captureUri();
assertThat(uri.getRawPath()).isEqualTo("/twitter/doc/1");
}
@Test // DATAES-488
public void deleteShouldEmitResponseCorrectly() {
hostProvider.when(HOST) //
.receiveDeleteOk();
client.delete(new DeleteRequest("twitter", "doc", "1")) //
.as(StepVerifier::create) //
.consumeNextWith(deleteResponse -> {
assertThat(deleteResponse.getResult()).isEqualTo(Result.DELETED);
assertThat(deleteResponse.getVersion()).isEqualTo(1);
assertThat(deleteResponse.getId()).isEqualTo("1");
assertThat(deleteResponse.getIndex()).isEqualTo("twitter");
}) //
.verifyComplete();
}
// --> SEARCH
@Test // DATAES-488
public void searchShouldHitSearchEndpoint() {
hostProvider.when(HOST) //
.receiveSearchOk();
client.search(new SearchRequest("twitter")).as(StepVerifier::create).verifyComplete();
verify(hostProvider.client(HOST)).method(HttpMethod.POST);
URI uri = hostProvider.when(HOST).captureUri();
assertThat(uri.getRawPath()).isEqualTo("/twitter/_search");
}
@Test // DATAES-488
public void searchShouldReturnSingleResultCorrectly() {
hostProvider.when(HOST) //
.receive(Receive::json) //
.body(fromPath("search-ok-single-hit"));
client.search(new SearchRequest("twitter")) //
.as(StepVerifier::create) //
.consumeNextWith(hit -> {
assertThat(hit.getId()).isEqualTo("2");
assertThat(hit.getIndex()).isEqualTo("twitter");
assertThat(hit.getSourceAsMap()) //
.containsEntry("user", "kimchy") //
.containsEntry("message", "Another tweet, will it be indexed?") //
.containsKey("post_date");
}).verifyComplete();
}
@Test // DATAES-488
public void searchShouldReturnMultipleResultsCorrectly() {
hostProvider.when(HOST) //
.receive(Receive::json) //
.body(fromPath("search-ok-multiple-hits"));
client.search(new SearchRequest("twitter")) //
.as(StepVerifier::create) //
.consumeNextWith(hit -> {
assertThat(hit.getId()).isEqualTo("2");
assertThat(hit.getIndex()).isEqualTo("twitter");
assertThat(hit.getSourceAsMap()) //
.containsEntry("user", "kimchy") //
.containsEntry("message", "Another tweet, will it be indexed?") //
.containsKey("post_date");
}) //
.consumeNextWith(hit -> {
assertThat(hit.getId()).isEqualTo("1");
assertThat(hit.getIndex()).isEqualTo("twitter");
assertThat(hit.getSourceAsMap()) //
.containsEntry("user", "kimchy") //
.containsEntry("message", "Trying out Elasticsearch, so far so good?") //
.containsKey("post_date");
}).verifyComplete();
}
@Test // DATAES-488
public void searchShouldReturnEmptyFluxIfNothingFound() {
hostProvider.when(HOST) //
.receiveSearchOk();
client.search(new SearchRequest("twitter")) //
.as(StepVerifier::create) //
.verifyComplete();
}
// --> AGGREGATE
@Test // DATAES-567
public void aggregateShouldHitSearchEndpoint() {
hostProvider.when(HOST) //
.receive(Receive::json) //
.body(fromPath("aggregate-ok-no-results"));
client.search(new SearchRequest("twitter")).as(StepVerifier::create).verifyComplete();
verify(hostProvider.client(HOST)).method(HttpMethod.POST);
URI uri = hostProvider.when(HOST).captureUri();
assertThat(uri.getRawPath()).isEqualTo("/twitter/_search");
}
@Test // DATAES-567
public void aggregateShouldReturnSingleResultCorrectly() {
hostProvider.when(HOST) //
.receive(Receive::json) //
.body(fromPath("aggregate-ok-single-result"));
client.aggregate(new SearchRequest("twitter")) //
.as(StepVerifier::create) //
.consumeNextWith(aggregation -> {
assertThat(aggregation.getName()).isEqualTo("users");
assertThat(aggregation instanceof ParsedStringTerms);
ParsedStringTerms parsedStringTerms = (ParsedStringTerms) aggregation;
assertThat(parsedStringTerms.getBuckets().size()).isEqualTo(2);
assertThat(parsedStringTerms.getBucketByKey("kimchy").getDocCount()).isEqualTo(2);
assertThat(parsedStringTerms.getBucketByKey("elastic").getDocCount()).isEqualTo(1);
}).verifyComplete();
}
@Test // DATAES-567
public void aggregateShouldReturnMultipleResultsCorrectly() {
hostProvider.when(HOST) //
.receive(Receive::json) //
.body(fromPath("aggregate-ok-multiple-results"));
client.aggregate(new SearchRequest("twitter")) //
.as(StepVerifier::create) //
.consumeNextWith(aggregation -> {
assertThat(aggregation.getName()).isEqualTo("users");
assertThat(aggregation instanceof ParsedStringTerms);
ParsedStringTerms parsedStringTerms = (ParsedStringTerms) aggregation;
assertThat(parsedStringTerms.getBuckets().size()).isEqualTo(2);
assertThat(parsedStringTerms.getBucketByKey("kimchy").getDocCount()).isEqualTo(2);
assertThat(parsedStringTerms.getBucketByKey("elastic").getDocCount()).isEqualTo(1);
}) //
.consumeNextWith(aggregation -> {
assertThat(aggregation.getName()).isEqualTo("max_post_date");
assertThat(aggregation instanceof ParsedMax);
ParsedMax parsedMax = (ParsedMax) aggregation;
assertThat(Instant.ofEpochMilli((long) parsedMax.getValue()))
.isEqualTo(Instant.parse("2010-01-15T01:46:38Z"));
}).verifyComplete();
}
@Test // DATAES-567
public void aggregateShouldReturnAggregationWithNoValuesWhenNoResultsFound() {
hostProvider.when(HOST) //
.receive(Receive::json) //
.body(fromPath("aggregate-ok-no-results"));
client.aggregate(new SearchRequest("twitter")) //
.as(StepVerifier::create) //
.consumeNextWith(aggregation -> {
assertThat(aggregation.getName()).isEqualTo("users");
assertThat(aggregation instanceof ParsedStringTerms);
ParsedStringTerms parsedStringTerms = (ParsedStringTerms) aggregation;
assertThat(parsedStringTerms.getBuckets().size()).isEqualTo(0);
}).verifyComplete();
}
// --> SCROLL
@Test // DATAES-510
public void scrollShouldReadAll() throws IOException {
byte[] start = StreamUtils.copyToByteArray(Receive.fromPath("search-ok-scroll").getInputStream());
byte[] next = StreamUtils.copyToByteArray(Receive.fromPath("scroll_ok").getInputStream());
byte[] end = StreamUtils.copyToByteArray(Receive.fromPath("scroll_no_more_results").getInputStream());
byte[] cleanup = StreamUtils.copyToByteArray(Receive.fromPath("scroll_clean").getInputStream());
hostProvider.when(HOST) //
.receive(Receive::json) //
.receive(response -> Mockito.when(response.body(any())).thenReturn(Mono.just(start), Mono.just(next),
Mono.just(end), Mono.just(cleanup)));
client.scroll(new SearchRequest("twitter")) //
.as(StepVerifier::create) //
.expectNextCount(4) //
.verifyComplete();
hostProvider.when(HOST).receive(response -> verify(response, times(4)).body(any()));
}
@Test // DATAES-510
public void scrollShouldCleanUpResourcesOnError() throws IOException {
byte[] start = StreamUtils.copyToByteArray(Receive.fromPath("search-ok-scroll").getInputStream());
byte[] error = StreamUtils.copyToByteArray(Receive.fromPath("scroll_error").getInputStream());
byte[] cleanup = StreamUtils.copyToByteArray(Receive.fromPath("scroll_clean").getInputStream());
hostProvider.when(HOST) //
.receive(Receive::json) //
.receive(response -> Mockito.when(response.body(any())).thenReturn(Mono.just(start), Mono.just(error),
Mono.just(cleanup)));
client.scroll(new SearchRequest("twitter")) //
.as(StepVerifier::create) //
.expectNextCount(2) //
.verifyError();
hostProvider.when(HOST).receive(response -> verify(response, times(3)).body(any()));
}
@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();
}
}

View File

@ -1,505 +0,0 @@
/*
* Copyright 2018-2023 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.erhlc;
import static org.mockito.Mockito.*;
import reactor.core.publisher.Mono;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
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.erhlc.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;
import org.springframework.util.StreamUtils;
import org.springframework.util.StringUtils;
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 Huw Ayling-Miller
* @author Henrique Amaral
* @author Peter-Josef Meisch
*/
public class ReactiveMockClientTestsUtils {
private final static Map<String, InetSocketAddress> ADDRESS_CACHE = new ConcurrentHashMap<>();
public static MockDelegatingElasticsearchHostProvider<SingleNodeHostProvider> single(String host) {
return provider(host);
}
public static MockDelegatingElasticsearchHostProvider<MultiNodeHostProvider> multi(String... hosts) {
return provider(hosts);
}
public static <T extends HostProvider<T>> MockDelegatingElasticsearchHostProvider<T> provider(String... hosts) {
ErrorCollector errorCollector = new ErrorCollector();
MockWebClientProvider clientProvider = new MockWebClientProvider(errorCollector);
T delegate;
if (hosts.length == 1) {
// noinspection unchecked
delegate = (T) new SingleNodeHostProvider(clientProvider, getInetSocketAddress(hosts[0])) {};
} else {
// noinspection unchecked
delegate = (T) new MultiNodeHostProvider(clientProvider, Arrays.stream(hosts)
.map(ReactiveMockClientTestsUtils::getInetSocketAddress).toArray(InetSocketAddress[]::new)) {};
}
return new MockDelegatingElasticsearchHostProvider<>(HttpHeaders.EMPTY, clientProvider, errorCollector, delegate,
null);
}
private static InetSocketAddress getInetSocketAddress(String hostAndPort) {
return ADDRESS_CACHE.computeIfAbsent(hostAndPort, ElasticsearchHost::parse);
}
public static class ErrorCollector implements Consumer<Throwable> {
List<Throwable> errors = new CopyOnWriteArrayList<>();
@Override
public void accept(Throwable throwable) {
errors.add(throwable);
}
List<Throwable> captured() {
return Collections.unmodifiableList(errors);
}
}
public static class MockDelegatingElasticsearchHostProvider<T extends HostProvider<T>> implements HostProvider<T> {
private final HttpHeaders httpHeaders;
private final T delegate;
private final MockWebClientProvider clientProvider;
private final ErrorCollector errorCollector;
private @Nullable final String activeDefaultHost;
public MockDelegatingElasticsearchHostProvider(HttpHeaders httpHeaders, MockWebClientProvider clientProvider,
ErrorCollector errorCollector, T delegate, @Nullable String activeDefaultHost) {
this.httpHeaders = httpHeaders;
this.errorCollector = errorCollector;
this.clientProvider = clientProvider;
this.delegate = delegate;
this.activeDefaultHost = activeDefaultHost;
}
@Override
public Mono<InetSocketAddress> lookupActiveHost() {
return delegate.lookupActiveHost();
}
@Override
public Mono<InetSocketAddress> lookupActiveHost(Verification verification) {
if (StringUtils.hasText(activeDefaultHost)) {
return Mono.just(getInetSocketAddress(activeDefaultHost));
}
return delegate.lookupActiveHost(verification);
}
@Override
public Mono<WebClient> getActive() {
return delegate.getActive();
}
@Override
public Mono<WebClient> getActive(Verification verification) {
return delegate.getActive(verification);
}
@Override
public WebClient createWebClient(InetSocketAddress endpoint) {
return delegate.createWebClient(endpoint);
}
@Override
public Mono<ClusterInformation> clusterInfo() {
if (StringUtils.hasText(activeDefaultHost)) {
return Mono.just(new ClusterInformation(
Collections.singleton(ElasticsearchHost.online(getInetSocketAddress(activeDefaultHost)))));
}
return delegate.clusterInfo();
}
public Send when(String host) {
return clientProvider.when(host);
}
public WebClient client(String host) {
return clientProvider.when(host).client();
}
public List<Throwable> errors() {
return errorCollector.captured();
}
public T getDelegate() {
return delegate;
}
public MockDelegatingElasticsearchHostProvider<T> withActiveDefaultHost(String host) {
return new MockDelegatingElasticsearchHostProvider<>(httpHeaders, clientProvider, errorCollector, delegate, host);
}
}
public static class MockWebClientProvider implements WebClientProvider {
private final Consumer<Throwable> errorListener;
private final Map<InetSocketAddress, WebClient> clientMap;
private final Map<InetSocketAddress, RequestHeadersUriSpec> headersUriSpecMap;
private final Map<InetSocketAddress, RequestBodyUriSpec> bodyUriSpecMap;
private final Map<InetSocketAddress, ClientResponse> responseMap;
public MockWebClientProvider(Consumer<Throwable> errorListener) {
this.errorListener = errorListener;
this.clientMap = new ConcurrentHashMap<>();
this.headersUriSpecMap = new LinkedHashMap<>();
this.bodyUriSpecMap = new LinkedHashMap<>();
this.responseMap = new LinkedHashMap<>();
}
public WebClient get(String host) {
return get(getInetSocketAddress(host));
}
@Override
public WebClient get(InetSocketAddress endpoint) {
return clientMap.computeIfAbsent(endpoint, key -> {
WebClient webClient = mock(WebClient.class);
RequestHeadersUriSpec headersUriSpec = mock(RequestHeadersUriSpec.class);
Mockito.when(headersUriSpec.uri(any(String.class))).thenReturn(headersUriSpec);
Mockito.when(headersUriSpec.uri(any(), any(Map.class))).thenReturn(headersUriSpec);
Mockito.when(headersUriSpec.headers(any(Consumer.class))).thenReturn(headersUriSpec);
Mockito.when(headersUriSpec.attribute(anyString(), anyString())).thenReturn(headersUriSpec);
Mockito.when(headersUriSpec.uri(any(Function.class))).thenReturn(headersUriSpec);
headersUriSpecMap.putIfAbsent(key, headersUriSpec);
ClientResponse response = mock(ClientResponse.class);
Mockito.when(response.statusCode()).thenReturn(HttpStatus.ACCEPTED);
Mockito.when(response.releaseBody()).thenReturn(Mono.empty());
Mockito.when(headersUriSpec.exchangeToMono(any())).thenAnswer(invocation -> {
final Function<ClientResponse, ? extends Mono<?>> responseHandler = invocation.getArgument(0);
if (responseHandler != null) {
return responseHandler.apply(response);
}
return Mono.empty();
});
responseMap.putIfAbsent(key, response);
RequestBodyUriSpec bodySpy = spy(WebClient.create().method(HttpMethod.POST));
Mockito.when(bodySpy.body(any())).thenReturn(headersUriSpec);
Mockito.when(bodySpy.exchangeToMono(any())).thenAnswer(invocation -> {
final Function<ClientResponse, ? extends Mono<?>> responseHandler = invocation.getArgument(0);
if (responseHandler != null) {
return responseHandler.apply(response);
}
return Mono.empty();
});
bodyUriSpecMap.putIfAbsent(key, bodySpy);
Mockito.when(webClient.get()).thenReturn(headersUriSpec);
Mockito.when(webClient.head()).thenReturn(headersUriSpec);
Mockito.when(webClient.method(any())).thenReturn(bodySpy);
return webClient;
});
}
@Override
public HttpHeaders getDefaultHeaders() {
return HttpHeaders.EMPTY;
}
@Override
public WebClientProvider withDefaultHeaders(HttpHeaders headers) {
throw new UnsupportedOperationException();
}
@Override
public Consumer<Throwable> getErrorListener() {
return errorListener;
}
@Override
public WebClientProvider withErrorListener(Consumer<Throwable> errorListener) {
throw new UnsupportedOperationException();
}
@Override
public String getPathPrefix() {
return null;
}
@Override
public WebClientProvider withPathPrefix(String pathPrefix) {
throw new UnsupportedOperationException();
}
@Override
public WebClientProvider withWebClientConfigurer(Function<WebClient, WebClient> webClientConfigurer) {
throw new UnsupportedOperationException("not implemented");
}
@Override
public WebClientProvider withRequestConfigurer(Consumer<WebClient.RequestHeadersSpec<?>> requestConfigurer) {
throw new UnsupportedOperationException("not implemented");
}
public Send when(String host) {
InetSocketAddress inetSocketAddress = getInetSocketAddress(host);
return new CallbackImpl(get(host), headersUriSpecMap.get(inetSocketAddress),
bodyUriSpecMap.get(inetSocketAddress), responseMap.get(inetSocketAddress));
}
public interface Client {
WebClient client();
}
@SuppressWarnings("UnusedReturnValue")
public interface Send extends Receive, Client {
Receive get(Consumer<RequestHeadersUriSpec<?>> headerSpec);
Receive exchange(Consumer<RequestBodyUriSpec> bodySpec);
default URI captureUri() {
Set<URI> capturingSet = new LinkedHashSet<>();
exchange(requestBodyUriSpec -> {
// noinspection unchecked
ArgumentCaptor<Function<UriBuilder, URI>> fkt = ArgumentCaptor.forClass(Function.class);
verify(requestBodyUriSpec).uri(fkt.capture());
capturingSet.add(fkt.getValue().apply(new DefaultUriBuilderFactory().builder()));
});
return capturingSet.iterator().next();
}
default Receive receiveJsonFromFile(String file) {
return receive(Receive::json) //
.body(Receive.fromPath(file));
}
default Receive receiveInfo() {
return receiveJsonFromFile("info") //
.receive(Receive::ok);
}
default Receive receiveIndexCreated() {
return receiveJsonFromFile("index-ok-created") //
.receive(Receive::ok);
}
default Receive receiveIndexUpdated() {
return receiveJsonFromFile("index-ok-updated") //
.receive(Receive::ok);
}
default Receive receiveSearchOk() {
return receiveJsonFromFile("search-ok-no-hits") //
.receive(Receive::ok);
}
default Receive receiveGetByIdNotFound() {
return receiveJsonFromFile("get-by-id-no-hit") //
.receive(
response -> Mockito.when(response.statusCode()).thenReturn(HttpStatus.ACCEPTED, HttpStatus.NOT_FOUND));
}
default Receive receiveGetById() {
return receiveJsonFromFile("get-by-id-ok") //
.receive(Receive::ok);
}
default Receive receiveUpdateOk() {
return receiveJsonFromFile("update-ok-updated") //
.receive(Receive::ok);
}
default Receive receiveDeleteOk() {
return receiveJsonFromFile("update-ok-deleted") //
.receive(Receive::ok);
}
default Receive updateFail() {
return receiveJsonFromFile("update-error-not-found") //
.receive(
response -> Mockito.when(response.statusCode()).thenReturn(HttpStatus.ACCEPTED, HttpStatus.NOT_FOUND));
}
default Receive receiveBulkOk() {
return receiveJsonFromFile("bulk-ok") //
.receive(Receive::ok);
}
}
public interface Receive {
Receive receive(Consumer<ClientResponse> response);
default Receive body(String json) {
return body(() -> json.getBytes(StandardCharsets.UTF_8));
}
default Receive body(Supplier<byte[]> json) {
return body(json.get());
}
default Receive body(Resource resource) {
return body(() -> {
try {
return StreamUtils.copyToByteArray(resource.getInputStream());
} catch (IOException e) {
throw new RuntimeException(e);
}
});
}
default Receive body(byte[] bytes) {
return receive(response -> Mockito.when(response.body(any())).thenReturn(Mono.just(bytes)));
}
static void ok(ClientResponse response) {
Mockito.when(response.statusCode()).thenReturn(HttpStatus.ACCEPTED);
}
static void error(ClientResponse response) {
Mockito.when(response.statusCode()).thenReturn(HttpStatus.INTERNAL_SERVER_ERROR);
}
static void notFound(ClientResponse response) {
Mockito.when(response.statusCode()).thenReturn(HttpStatus.NOT_FOUND);
}
static void json(ClientResponse response) {
ClientResponse.Headers headers = Mockito.mock(ClientResponse.Headers.class);
Mockito.when(headers.contentType()).thenReturn(Optional.of(MediaType.APPLICATION_JSON));
Mockito.when(response.headers()).thenReturn(headers);
}
static Resource fromPath(String filename) {
return new ClassPathResource("/org/springframework/data/elasticsearch/client/" + filename + ".json");
}
}
static class CallbackImpl implements Send, Receive {
WebClient client;
RequestHeadersUriSpec<?> headersUriSpec;
RequestBodyUriSpec bodyUriSpec;
ClientResponse responseDelegate;
public CallbackImpl(WebClient client, RequestHeadersUriSpec<?> headersUriSpec, RequestBodyUriSpec bodyUriSpec,
ClientResponse responseDelegate) {
this.client = client;
this.headersUriSpec = headersUriSpec;
this.bodyUriSpec = bodyUriSpec;
this.responseDelegate = responseDelegate;
}
@Override
public Receive get(Consumer<RequestHeadersUriSpec<?>> uriSpec) {
uriSpec.accept(headersUriSpec);
return this;
}
@Override
public Receive exchange(Consumer<RequestBodyUriSpec> bodySpec) {
bodySpec.accept(this.bodyUriSpec);
return this;
}
@Override
public Receive receive(Consumer<ClientResponse> response) {
response.accept(responseDelegate);
return this;
}
@Override
public WebClient client() {
return client;
}
}
}
}

View File

@ -1,89 +0,0 @@
/*
* Copyright 2018-2023 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.erhlc;
import static org.assertj.core.api.Assertions.*;
import java.util.Collections;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.client.Request;
import org.junit.jupiter.api.Test;
/**
* Unit tests for {@link RequestConverters}.
*
* @author Roman Puchkovskiy
* @author Mark Paluch
*/
public class RequestConvertersTest {
@Test // DATAES-652
public void shouldNotAddIfSeqNoAndIfPrimaryTermToResultIfInputDoesNotcontainThemWhenConvertingIndexRequest() {
IndexRequest request = createMinimalIndexRequest();
Request result = RequestConverters.index(request);
assertThat(result.getParameters()).doesNotContainKeys("if_seq_no", "if_primary_term");
}
private IndexRequest createMinimalIndexRequest() {
IndexRequest request = new IndexRequest("the-index", "the-type", "id");
request.source(Collections.singletonMap("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()).containsEntry("if_seq_no", "3").containsEntry("if_primary_term", "4");
}
@Test // DATAES-652
public void shouldNotAddIfSeqNoAndIfPrimaryTermToResultIfInputDoesNotcontainThemWhenConvertingDeleteRequest() {
DeleteRequest request = createMinimalDeleteRequest();
Request result = RequestConverters.delete(request);
assertThat(result.getParameters()).doesNotContainKeys("if_seq_no", "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()).containsEntry("if_seq_no", "3");
assertThat(result.getParameters()).containsEntry("if_primary_term", "4");
}
}

View File

@ -1,895 +0,0 @@
/*
* Copyright 2020-2023 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.erhlc;
import static org.assertj.core.api.Assertions.*;
import static org.elasticsearch.index.query.QueryBuilders.*;
import static org.skyscreamer.jsonassert.JSONAssert.*;
import java.io.IOException;
import java.time.Duration;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import org.elasticsearch.action.DocWriteRequest;
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.client.indices.PutIndexTemplateRequest;
import org.elasticsearch.common.lucene.search.function.CombineFunction;
import org.elasticsearch.common.lucene.search.function.FunctionScoreQuery;
import org.elasticsearch.common.xcontent.XContentHelper;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder.FilterFunctionBuilder;
import org.elasticsearch.index.query.functionscore.GaussDecayFunctionBuilder;
import org.elasticsearch.search.fetch.subphase.FetchSourceContext;
import org.elasticsearch.xcontent.ToXContent;
import org.elasticsearch.xcontent.XContentType;
import org.json.JSONException;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.core.convert.support.GenericConversionService;
import org.springframework.data.annotation.Id;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.core.convert.MappingElasticsearchConverter;
import org.springframework.data.elasticsearch.core.geo.GeoPoint;
import org.springframework.data.elasticsearch.core.index.AliasAction;
import org.springframework.data.elasticsearch.core.index.AliasActionParameters;
import org.springframework.data.elasticsearch.core.index.AliasActions;
import org.springframework.data.elasticsearch.core.index.PutTemplateRequest;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.mapping.SimpleElasticsearchMappingContext;
import org.springframework.data.elasticsearch.core.query.*;
import org.springframework.data.elasticsearch.core.query.RescorerQuery.ScoreMode;
import org.springframework.data.elasticsearch.core.reindex.ReindexRequest;
import org.springframework.data.elasticsearch.core.reindex.Remote;
import org.springframework.lang.Nullable;
/**
* @author Peter-Josef Meisch
* @author Roman Puchkovskiy
* @author Peer Mueller
* @author vdisk
* @author Sijia Liu
* @author Peter Nowak
*/
@SuppressWarnings("ConstantConditions")
class RequestFactoryTests {
@Nullable private static RequestFactory requestFactory;
@Nullable private static MappingElasticsearchConverter converter;
@BeforeAll
static void setUpAll() {
SimpleElasticsearchMappingContext mappingContext = new SimpleElasticsearchMappingContext();
mappingContext.setInitialEntitySet(new HashSet<>(Arrays.asList(Person.class, EntityWithSeqNoPrimaryTerm.class)));
mappingContext.afterPropertiesSet();
converter = new MappingElasticsearchConverter(mappingContext, new GenericConversionService());
converter.afterPropertiesSet();
requestFactory = new RequestFactory((converter));
}
@Test // DATAES-187
public void shouldUsePageableOffsetToSetFromInSearchRequest() {
// given
Pageable pageable = new PageRequest(1, 10, Sort.unsorted()) {
@Override
public long getOffset() {
return 30;
}
};
NativeSearchQuery query = new NativeSearchQueryBuilder() //
.withPageable(pageable) //
.build();
// when
SearchRequest searchRequest = requestFactory.searchRequest(query, null, null, IndexCoordinates.of("test"));
// then
assertThat(searchRequest.source().from()).isEqualTo(30);
}
@Test // DATAES-227
public void shouldUseUpsertOnUpdate() {
// given
Map<String, Object> doc = new HashMap<>();
doc.put("id", "1");
doc.put("message", "test");
org.springframework.data.elasticsearch.core.document.Document document = org.springframework.data.elasticsearch.core.document.Document
.from(doc);
UpdateQuery updateQuery = UpdateQuery.builder("1") //
.withDocument(document) //
.withUpsert(document) //
.build();
// when
UpdateRequest request = requestFactory.updateRequest(updateQuery, IndexCoordinates.of("index"));
// then
assertThat(request).isNotNull();
assertThat(request.upsertRequest()).isNotNull();
}
@Test // #2287
@DisplayName("should create update query when script language is not set")
void shouldCreateUpdateQueryWhenScriptTypeIsSetToNull() {
UpdateQuery updateQuery = UpdateQuery.builder("1") //
.withScript("script").build();
UpdateRequest request = requestFactory.updateRequest(updateQuery, IndexCoordinates.of("index"));
assertThat(request).isNotNull();
}
@Test // DATAES-693
public void shouldReturnSourceWhenRequested() {
// given
Map<String, Object> doc = new HashMap<>();
doc.put("id", "1");
doc.put("message", "test");
org.springframework.data.elasticsearch.core.document.Document document = org.springframework.data.elasticsearch.core.document.Document
.from(doc);
UpdateQuery updateQuery = UpdateQuery.builder("1") //
.withDocument(document) //
.withFetchSource(true) //
.build();
// when
UpdateRequest request = requestFactory.updateRequest(updateQuery, IndexCoordinates.of("index"));
// then
assertThat(request).isNotNull();
assertThat(request.fetchSource()).isEqualTo(FetchSourceContext.FETCH_SOURCE);
}
@Test
// DATAES-734
void shouldBuildSearchWithGeoSortSort() throws JSONException {
CriteriaQuery query = new CriteriaQuery(new Criteria("lastName").is("Smith"));
Sort sort = Sort.by(new GeoDistanceOrder("location", new GeoPoint(49.0, 8.4)));
query.addSort(sort);
converter.updateQuery(query, Person.class);
String expected = '{' + //
" \"query\": {" + //
" \"bool\": {" + //
" \"must\": [" + //
" {" + //
" \"query_string\": {" + //
" \"query\": \"Smith\"," + //
" \"fields\": [" + //
" \"last-name^1.0\"" + //
" ]" + //
" }" + //
" }" + //
" ]" + //
" }" + //
" }," + //
" \"sort\": [" + //
" {" + //
" \"_geo_distance\": {" + //
" \"current-location\": [" + //
" {" + //
" \"lat\": 49.0," + //
" \"lon\": 8.4" + //
" }" + //
" ]," + //
" \"unit\": \"m\"," + //
" \"distance_type\": \"arc\"," + //
" \"order\": \"asc\"," + //
" \"mode\": \"min\"," + //
" \"ignore_unmapped\": false" + //
" }" + //
" }" + //
" ]" + //
'}';
String searchRequest = requestFactory.searchRequest(query, null, Person.class, IndexCoordinates.of("persons"))
.source().toString();
assertEquals(expected, searchRequest, false);
}
@Test
// DATAES-449
void shouldAddRoutingFromQuery() {
String route = "route66";
CriteriaQuery query = new CriteriaQuery(new Criteria("lastName").is("Smith"));
query.setRoute(route);
converter.updateQuery(query, Person.class);
SearchRequest searchRequest = requestFactory.searchRequest(query, null, Person.class,
IndexCoordinates.of("persons"));
assertThat(searchRequest.routing()).isEqualTo(route);
}
@Test
// #2087
void shouldAddRoutingFromRouting() {
String route = "route66";
CriteriaQuery query = new CriteriaQuery(new Criteria("lastName").is("Smith"));
converter.updateQuery(query, Person.class);
SearchRequest searchRequest = requestFactory.searchRequest(query, route, Person.class,
IndexCoordinates.of("persons"));
assertThat(searchRequest.routing()).isEqualTo(route);
}
@Test
// DATAES-765
void shouldAddMaxQueryWindowForUnpagedToRequest() {
Query query = new NativeSearchQueryBuilder().withQuery(matchAllQuery()).withPageable(Pageable.unpaged()).build();
SearchRequest searchRequest = requestFactory.searchRequest(query, null, Person.class,
IndexCoordinates.of("persons"));
assertThat(searchRequest.source().from()).isEqualTo(0);
assertThat(searchRequest.source().size()).isEqualTo(RequestFactory.INDEX_MAX_RESULT_WINDOW);
}
@Test
// DATAES-799
void shouldIncludeSeqNoAndPrimaryTermFromIndexQueryToIndexRequest() {
IndexQuery query = new IndexQuery();
query.setObject(new Person());
query.setSeqNo(1L);
query.setPrimaryTerm(2L);
IndexRequest request = requestFactory.indexRequest(query, IndexCoordinates.of("persons"));
assertThat(request.ifSeqNo()).isEqualTo(1L);
assertThat(request.ifPrimaryTerm()).isEqualTo(2L);
}
@Test
// DATAES-799
void shouldNotRequestSeqNoAndPrimaryTermWhenEntityClassDoesNotContainSeqNoPrimaryTermProperty() {
Query query = new NativeSearchQueryBuilder().build();
SearchRequest request = requestFactory.searchRequest(query, null, Person.class, IndexCoordinates.of("persons"));
assertThat(request.source().seqNoAndPrimaryTerm()).isNull();
}
@Test
// DATAES-799
void shouldRequestSeqNoAndPrimaryTermWhenEntityClassContainsSeqNoPrimaryTermProperty() {
Query query = new NativeSearchQueryBuilder().build();
SearchRequest request = requestFactory.searchRequest(query, null, EntityWithSeqNoPrimaryTerm.class,
IndexCoordinates.of("seqNoPrimaryTerm"));
assertThat(request.source().seqNoAndPrimaryTerm()).isTrue();
}
@Test
// DATAES-799
void shouldNotRequestSeqNoAndPrimaryTermWhenEntityClassIsNull() {
Query query = new NativeSearchQueryBuilder().build();
SearchRequest request = requestFactory.searchRequest(query, null, null, IndexCoordinates.of("persons"));
assertThat(request.source().seqNoAndPrimaryTerm()).isNull();
}
@Test
// DATAES-864
void shouldBuildIndicesAliasRequest() throws IOException, JSONException {
AliasActions aliasActions = new AliasActions();
aliasActions.add(new AliasAction.Add(
AliasActionParameters.builder().withIndices("index1", "index2").withAliases("alias1").build()));
aliasActions.add(
new AliasAction.Remove(AliasActionParameters.builder().withIndices("index3").withAliases("alias1").build()));
aliasActions.add(new AliasAction.RemoveIndex(AliasActionParameters.builder().withIndices("index3").build()));
aliasActions.add(new AliasAction.Add(AliasActionParameters.builder().withIndices("index4").withAliases("alias4")
.withRouting("routing").withIndexRouting("indexRouting").withSearchRouting("searchRouting").withIsHidden(true)
.withIsWriteIndex(true).build()));
Query query = new CriteriaQuery(new Criteria("lastName").is("Smith"));
aliasActions.add(new AliasAction.Add(AliasActionParameters.builder().withIndices("index5").withAliases("alias5")
.withFilterQuery(query, Person.class).build()));
String expected = """
{
"actions": [
{
"add": {
"indices": [
"index1",
"index2"
],
"aliases": [
"alias1"
]
}
},
{
"remove": {
"indices": [
"index3"
],
"aliases": [
"alias1"
]
}
},
{
"remove_index": {
"indices": [
"index3"
]
}
},
{
"add": {
"indices": [
"index4"
],
"aliases": [
"alias4"
],
"routing": "routing",
"index_routing": "indexRouting",
"search_routing": "searchRouting",
"is_write_index": true,
"is_hidden": true
}
},
{
"add": {
"indices": [
"index5"
],
"aliases": [
"alias5"
],
"filter": {
"bool": {
"must": [
{
"query_string": {
"query": "Smith",
"fields": [
"last-name^1.0"
],
"type": "best_fields",
"default_operator": "and",
"max_determinized_states": 10000,
"enable_position_increments": true,
"fuzziness": "AUTO",
"fuzzy_prefix_length": 0,
"fuzzy_max_expansions": 50,
"phrase_slop": 0,
"escape": false,
"auto_generate_synonyms_phrase_query": true,
"fuzzy_transpositions": true,
"boost": 1.0
}
}
],
"adjust_pure_negative": true,
"boost": 1.0
}
}
}
}
]
}"""; //
IndicesAliasesRequest indicesAliasesRequest = requestFactory.indicesAliasesRequest(aliasActions);
String json = requestToString(indicesAliasesRequest);
assertEquals(expected, json, false);
}
@Test
// DATAES-612
void shouldCreatePutIndexTemplateRequest() throws JSONException, IOException {
String expected = """
{
"index_patterns": [
"test-*"
],
"order": 42,
"version": 7,
"settings": {
"index": {
"number_of_replicas": "2",
"number_of_shards": "3",
"refresh_interval": "7s",
"store": {
"type": "oops"
}
}
},
"mappings": {
"properties": {
"price": {
"type": "double"
}
}
},
"aliases":{
"alias1": {},
"alias2": {},
"alias3": {
"routing": "11"
}
}
}
"""; //
org.springframework.data.elasticsearch.core.document.Document settings = org.springframework.data.elasticsearch.core.document.Document
.create();
settings.put("index.number_of_replicas", 2);
settings.put("index.number_of_shards", 3);
settings.put("index.refresh_interval", "7s");
settings.put("index.store.type", "oops");
org.springframework.data.elasticsearch.core.document.Document mappings = org.springframework.data.elasticsearch.core.document.Document
.parse("{\"properties\":{\"price\":{\"type\":\"double\"}}}");
AliasActions aliasActions = new AliasActions(
new AliasAction.Add(AliasActionParameters.builderForTemplate().withAliases("alias1", "alias2").build()),
new AliasAction.Add(
AliasActionParameters.builderForTemplate().withAliases("alias3").withRouting("11").build()));
PutTemplateRequest putTemplateRequest = PutTemplateRequest.builder("test-template", "test-*") //
.withSettings(settings) //
.withMappings(mappings) //
.withAliasActions(aliasActions) //
.withOrder(42) //
.withVersion(7) //
.build(); //
PutIndexTemplateRequest putIndexTemplateRequest = requestFactory.putIndexTemplateRequest(putTemplateRequest);
String json = requestToString(putIndexTemplateRequest);
assertEquals(expected, json, false);
}
@Test // DATAES-247
@DisplayName("should set op_type INDEX if not specified")
void shouldSetOpTypeIndexIfNotSpecifiedAndIdIsSet() {
IndexQuery indexQuery = new IndexQueryBuilder().withId("42").withObject(new Person("42", "Smith")).build();
IndexRequest indexRequest = requestFactory.indexRequest(indexQuery, IndexCoordinates.of("optype"));
assertThat(indexRequest.opType()).isEqualTo(DocWriteRequest.OpType.INDEX);
}
@Test // DATAES-247
@DisplayName("should set op_type CREATE if specified")
void shouldSetOpTypeCreateIfSpecified() {
IndexQuery indexQuery = new IndexQueryBuilder().withOpType(IndexQuery.OpType.CREATE).withId("42")
.withObject(new Person("42", "Smith")).build();
IndexRequest indexRequest = requestFactory.indexRequest(indexQuery, IndexCoordinates.of("optype"));
assertThat(indexRequest.opType()).isEqualTo(DocWriteRequest.OpType.CREATE);
}
@Test // DATAES-247
@DisplayName("should set op_type INDEX if specified")
void shouldSetOpTypeIndexIfSpecified() {
IndexQuery indexQuery = new IndexQueryBuilder().withOpType(IndexQuery.OpType.INDEX).withId("42")
.withObject(new Person("42", "Smith")).build();
IndexRequest indexRequest = requestFactory.indexRequest(indexQuery, IndexCoordinates.of("optype"));
assertThat(indexRequest.opType()).isEqualTo(DocWriteRequest.OpType.INDEX);
}
@Test // DATAES-1003
@DisplayName("should set timeout to request")
void shouldSetTimeoutToRequest() {
Query query = new NativeSearchQueryBuilder().withQuery(matchAllQuery()).withTimeout(Duration.ofSeconds(1)).build();
SearchRequest searchRequest = requestFactory.searchRequest(query, null, Person.class,
IndexCoordinates.of("persons"));
assertThat(searchRequest.source().timeout().getMillis()).isEqualTo(Duration.ofSeconds(1).toMillis());
}
private String requestToString(ToXContent request) throws IOException {
return XContentHelper.toXContent(request, XContentType.JSON, true).utf8ToString();
}
@Test
// #1686
void shouldBuildSearchWithRescorerQuery() throws JSONException {
CriteriaQuery query = new CriteriaQuery(new Criteria("lastName").is("Smith"));
RescorerQuery rescorerQuery = new RescorerQuery(new NativeSearchQueryBuilder() //
.withQuery(QueryBuilders
.functionScoreQuery(new FunctionScoreQueryBuilder.FilterFunctionBuilder[] {
new FilterFunctionBuilder(QueryBuilders.existsQuery("someField"),
new GaussDecayFunctionBuilder("someField", 0, 100000.0, null, 0.683).setWeight(5.022317f)),
new FilterFunctionBuilder(QueryBuilders.existsQuery("anotherField"),
new GaussDecayFunctionBuilder("anotherField", "202102", "31536000s", null, 0.683)
.setWeight(4.170836f)) })
.scoreMode(FunctionScoreQuery.ScoreMode.SUM).maxBoost(50.0f).boostMode(CombineFunction.AVG).boost(1.5f))
.build()).withWindowSize(50).withQueryWeight(2.0f).withRescoreQueryWeight(5.0f)
.withScoreMode(ScoreMode.Multiply);
RescorerQuery anotherRescorerQuery = new RescorerQuery(new NativeSearchQueryBuilder() //
.withQuery(QueryBuilders.matchPhraseQuery("message", "the quick brown").slop(2)).build()).withWindowSize(100)
.withQueryWeight(0.7f).withRescoreQueryWeight(1.2f);
query.addRescorerQuery(rescorerQuery);
query.addRescorerQuery(anotherRescorerQuery);
converter.updateQuery(query, Person.class);
String expected = """
{ "query": { "bool": { "must": [ { "query_string": { "query": "Smith", "fields": [ "last-name^1.0" ] } } ] } }, "rescore": [{
"window_size" : 100,
"query" : {
"rescore_query" : {
"match_phrase" : {
"message" : {
"query" : "the quick brown",
"slop" : 2
}
}
},
"query_weight" : 0.7,
"rescore_query_weight" : 1.2
}
}, {
"window_size": 50,
"query": {
"rescore_query": {
"function_score": {
"query": {
"match_all": {
"boost": 1.0
}
},
"functions": [
{
"filter": {
"exists": {
"field": "someField",
"boost": 1.0
}
},
"weight": 5.022317,
"gauss": {
"someField": {
"origin": 0.0,
"scale": 100000.0,
"decay": 0.683
},
"multi_value_mode": "MIN"
}
},
{
"filter": {
"exists": {
"field": "anotherField",
"boost": 1.0
}
},
"weight": 4.170836,
"gauss": {
"anotherField": {
"origin": "202102",
"scale": "31536000s",
"decay": 0.683
},
"multi_value_mode": "MIN"
}
}
],
"score_mode": "sum",
"boost_mode": "avg",
"max_boost": 50.0,
"boost": 1.5
}
},
"query_weight": 2.0, "rescore_query_weight": 5.0, "score_mode": "multiply" }
}
]
}""";
String searchRequest = requestFactory.searchRequest(query, null, Person.class, IndexCoordinates.of("persons"))
.source().toString();
assertEquals(expected, searchRequest, false);
}
@Test // #1564
@DisplayName("should not set request_cache on default SearchRequest")
void shouldNotSetRequestCacheOnDefaultSearchRequest() {
Query query = new NativeSearchQueryBuilder().withQuery(matchAllQuery()).build();
SearchRequest searchRequest = requestFactory.searchRequest(query, null, Person.class,
IndexCoordinates.of("persons"));
assertThat(searchRequest.requestCache()).isNull();
}
@Test // #1564
@DisplayName("should set request_cache true on SearchRequest")
void shouldSetRequestCacheTrueOnSearchRequest() {
Query query = new NativeSearchQueryBuilder().withQuery(matchAllQuery()).build();
query.setRequestCache(true);
SearchRequest searchRequest = requestFactory.searchRequest(query, null, Person.class,
IndexCoordinates.of("persons"));
assertThat(searchRequest.requestCache()).isTrue();
}
@Test // #1564
@DisplayName("should set request_cache false on SearchRequest")
void shouldSetRequestCacheFalseOnSearchRequest() {
Query query = new NativeSearchQueryBuilder().withQuery(matchAllQuery()).build();
query.setRequestCache(false);
SearchRequest searchRequest = requestFactory.searchRequest(query, null, Person.class,
IndexCoordinates.of("persons"));
assertThat(searchRequest.requestCache()).isFalse();
}
@Test // #2004
@DisplayName("should set stored fields on SearchRequest")
void shouldSetStoredFieldsOnSearchRequest() {
Query query = new NativeSearchQueryBuilder().withQuery(matchAllQuery()).withStoredFields("lastName", "location")
.build();
SearchRequest searchRequest = requestFactory.searchRequest(query, null, Person.class,
IndexCoordinates.of("persons"));
assertThat(searchRequest.source().storedFields()).isNotNull();
assertThat(searchRequest.source().storedFields().fieldNames())
.isEqualTo(Arrays.asList("last-name", "current-location"));
}
@Test
// #1529
void shouldCreateReindexRequest() throws IOException, JSONException {
final String expected = """
{
"source":{
"remote":{
"username":"admin",
"password":"password",
"host":"http://localhost:9200/elasticsearch",
"socket_timeout":"30s",
"connect_timeout":"30s"
},
"index":["source_1","source_2"],
"size":5,
"query":{"match_all":{}},
"_source":{"includes":["name"],"excludes":[]},
"slice":{"id":1,"max":20}
},
"dest":{
"index":"destination",
"routing":"routing",
"op_type":"create",
"pipeline":"pipeline",
"version_type":"external"
},
"max_docs":10,
"script":{"source":"Math.max(1,2)","lang":"java"},
"conflicts":"proceed"
}""";
Remote remote = Remote.builder("http", "localhost", 9200).withPathPrefix("elasticsearch").withUsername("admin")
.withPassword("password").withConnectTimeout(Duration.ofSeconds(30)).withSocketTimeout(Duration.ofSeconds(30))
.build();
ReindexRequest reindexRequest = ReindexRequest
.builder(IndexCoordinates.of("source_1", "source_2"), IndexCoordinates.of("destination"))
.withConflicts(ReindexRequest.Conflicts.PROCEED).withMaxDocs(10L)
.withSourceQuery(new NativeSearchQueryBuilder().withQuery(matchAllQuery()).build()).withSourceSize(5)
.withSourceSourceFilter(new FetchSourceFilterBuilder().withIncludes("name").build()).withSourceRemote(remote)
.withSourceSlice(1, 20).withDestOpType(IndexQuery.OpType.CREATE)
.withDestVersionType(Document.VersionType.EXTERNAL).withDestPipeline("pipeline").withDestRouting("routing")
.withScript("Math.max(1,2)", "java").build();
final String json = requestToString(requestFactory.reindexRequest(reindexRequest));
assertEquals(expected, json, false);
}
@Test
// #1529
void shouldAllowSourceQueryForReindexWithoutRemote() throws IOException, JSONException {
final String expected = """
{
"source":{
"index":["source"],
"query":{"match_all":{}}
},
"dest":{
"index":"destination",
"op_type":"index",
"version_type":"internal"
}
}""";
ReindexRequest reindexRequest = ReindexRequest
.builder(IndexCoordinates.of("source"), IndexCoordinates.of("destination"))
.withSourceQuery(new NativeSearchQueryBuilder().withQuery(matchAllQuery()).build()).build();
final String json = requestToString(requestFactory.reindexRequest(reindexRequest));
assertEquals(expected, json, false);
}
@Test // #2075
@DisplayName("should not fail on empty Option set during toElasticsearchIndicesOptions")
void shouldNotFailOnEmptyOptionsOnToElasticsearchIndicesOptions() {
assertThat(requestFactory.toElasticsearchIndicesOptions(new IndicesOptions(
EnumSet.noneOf(IndicesOptions.Option.class), EnumSet.of(IndicesOptions.WildcardStates.OPEN)))).isNotNull();
}
@Test // #2075
@DisplayName("should not fail on empty WildcardState set during toElasticsearchIndicesOptions")
void shouldNotFailOnEmptyWildcardStatesOnToElasticsearchIndicesOptions() {
assertThat(requestFactory.toElasticsearchIndicesOptions(IndicesOptions.STRICT_SINGLE_INDEX_NO_EXPAND_FORBID_CLOSED))
.isNotNull();
}
@Test // #2043
@DisplayName("should use index name from query if set in bulk index")
void shouldUseIndexNameFromQueryIfSetInBulkIndex() {
String queryIndexName = "query-index-name";
String methodIndexName = "method-index-name";
IndexQuery indexQuery = new IndexQueryBuilder().withIndex(queryIndexName).withId("42").withObject(new Person())
.build();
IndexRequest indexRequest = requestFactory.indexRequest(indexQuery, IndexCoordinates.of(methodIndexName));
assertThat(indexRequest.index()).isEqualTo(queryIndexName);
}
@Test // #2043
@DisplayName("should use index name from method if none is set in query in bulk index")
void shouldUseIndexNameFromMethodIfNoneIsSetInQueryInBulkIndex() {
String methodIndexName = "method-index-name";
IndexQuery indexQuery = new IndexQueryBuilder().withId("42").withObject(new Person()).build();
IndexRequest indexRequest = requestFactory.indexRequest(indexQuery, IndexCoordinates.of(methodIndexName));
assertThat(indexRequest.index()).isEqualTo(methodIndexName);
}
@Test // #2043
@DisplayName("should use index name from query if set in bulk update")
void shouldUseIndexNameFromQueryIfSetInBulkUpdate() {
String queryIndexName = "query-index-name";
String methodIndexName = "method-index-name";
UpdateQuery updateQuery = UpdateQuery.builder("42").withIndex(queryIndexName)
.withDocument(org.springframework.data.elasticsearch.core.document.Document.create()).build();
UpdateRequest updateRequest = requestFactory.updateRequest(updateQuery, IndexCoordinates.of(methodIndexName));
assertThat(updateRequest.index()).isEqualTo(queryIndexName);
}
@Test // #2043
@DisplayName("should use index name from method if none is set in query in bulk update")
void shouldUseIndexNameFromMethodIfNoneIsSetInQueryInBulkUpdate() {
String methodIndexName = "method-index-name";
UpdateQuery updateQuery = UpdateQuery.builder("42")
.withDocument(org.springframework.data.elasticsearch.core.document.Document.create()).build();
UpdateRequest updateRequest = requestFactory.updateRequest(updateQuery, IndexCoordinates.of(methodIndexName));
assertThat(updateRequest.index()).isEqualTo(methodIndexName);
}
// region entities
static class Person {
@Nullable
@Id String id;
@Nullable
@Field(name = "last-name") String lastName;
@Nullable
@Field(name = "current-location") GeoPoint location;
public Person() {}
public Person(@Nullable String id, @Nullable String lastName) {
this.id = id;
this.lastName = lastName;
}
public Person(@Nullable String id, @Nullable String lastName, @Nullable GeoPoint location) {
this.id = id;
this.lastName = lastName;
this.location = location;
}
@Nullable
public String getId() {
return id;
}
public void setId(@Nullable String id) {
this.id = id;
}
@Nullable
public String getLastName() {
return lastName;
}
public void setLastName(@Nullable String lastName) {
this.lastName = lastName;
}
@Nullable
public GeoPoint getLocation() {
return location;
}
public void setLocation(@Nullable GeoPoint location) {
this.location = location;
}
}
static class EntityWithSeqNoPrimaryTerm {
@Nullable private SeqNoPrimaryTerm seqNoPrimaryTerm;
}
// endregion
}

View File

@ -1,76 +0,0 @@
/*
* Copyright 2018-2023 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.erhlc;
import static org.assertj.core.api.Assertions.*;
import reactor.test.StepVerifier;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.data.elasticsearch.client.ElasticsearchHost;
import org.springframework.data.elasticsearch.client.ElasticsearchHost.State;
import org.springframework.data.elasticsearch.client.NoReachableHostException;
import org.springframework.data.elasticsearch.client.erhlc.ReactiveMockClientTestsUtils.MockDelegatingElasticsearchHostProvider;
import org.springframework.data.elasticsearch.client.erhlc.ReactiveMockClientTestsUtils.MockWebClientProvider.Receive;
/**
* @author Christoph Strobl
* @author Peter-Josef Meisch
*/
public class SingleNodeHostProviderUnitTests {
static final String HOST_1 = ":9200";
MockDelegatingElasticsearchHostProvider<SingleNodeHostProvider> mock;
SingleNodeHostProvider provider;
@BeforeEach
public void setUp() {
mock = ReactiveMockClientTestsUtils.single(HOST_1);
provider = mock.getDelegate();
}
@Test // DATAES-488
public void refreshHostStateShouldUpdateNodeStateCorrectly() {
mock.when(HOST_1).receive(Receive::error);
provider.clusterInfo().as(StepVerifier::create).expectNextCount(1).verifyComplete();
assertThat(provider.getCachedHostState()).extracting(ElasticsearchHost::getState).isEqualTo(State.OFFLINE);
}
@Test // DATAES-488
public void getActiveReturnsFirstActiveHost() {
mock.when(HOST_1).receive(Receive::ok);
provider.clusterInfo().as(StepVerifier::create).expectNextCount(1).verifyComplete();
assertThat(provider.getCachedHostState()).extracting(ElasticsearchHost::getState).isEqualTo(State.ONLINE);
}
@Test // DATAES-488
public void getActiveErrorsWhenNoActiveHostFound() {
mock.when(HOST_1).receive(Receive::error);
provider.getActive().as(StepVerifier::create).expectError(NoReachableHostException.class);
}
}

View File

@ -1,30 +0,0 @@
/*
* Copyright 2020-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.elasticsearch.config;
import org.springframework.context.annotation.Import;
import org.springframework.data.elasticsearch.junit.jupiter.ElasticsearchRestTemplateConfiguration;
import org.springframework.test.context.ContextConfiguration;
/**
* @author Peter-Josef Meisch
*/
@ContextConfiguration(classes = { AuditingERHLCIntegrationTests.Config.class })
public class AuditingERHLCIntegrationTests extends AuditingIntegrationTests {
@Import({ ElasticsearchRestTemplateConfiguration.class, AuditingIntegrationTests.Config.class })
static class Config {}
}

View File

@ -1,33 +0,0 @@
/*
* Copyright 2021-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.elasticsearch.config;
import org.springframework.context.annotation.Import;
import org.springframework.data.elasticsearch.junit.jupiter.ReactiveElasticsearchRestTemplateConfiguration;
import org.springframework.test.context.ContextConfiguration;
/**
* @author Peter-Josef Meisch
* @since 4.4
*/
@ContextConfiguration(classes = { AuditingReactiveERHLCIntegrationTests.Config.class })
public class AuditingReactiveERHLCIntegrationTests extends AuditingReactiveIntegrationTest {
@Import({ ReactiveElasticsearchRestTemplateConfiguration.class, AuditingReactiveIntegrationTest.Config.class })
static class Config {
}
}

View File

@ -16,23 +16,15 @@
package org.springframework.data.elasticsearch.config;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.Mockito.*;
import java.util.Collection;
import java.util.Collections;
import org.elasticsearch.client.RestHighLevelClient;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.client.erhlc.AbstractElasticsearchConfiguration;
import org.springframework.data.elasticsearch.client.erhlc.AbstractReactiveElasticsearchConfiguration;
import org.springframework.data.elasticsearch.client.erhlc.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.client.erhlc.ReactiveElasticsearchClient;
import org.springframework.data.elasticsearch.client.erhlc.ReactiveElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
import org.springframework.data.elasticsearch.core.mapping.SimpleElasticsearchMappingContext;
@ -79,53 +71,11 @@ public class ElasticsearchConfigurationSupportUnitTests {
assertThat(context.getBean(ElasticsearchConverter.class)).isNotNull();
}
@Test // DATAES-504
public void restConfigContainsElasticsearchTemplate() {
AbstractApplicationContext context = new AnnotationConfigApplicationContext(RestConfig.class);
assertThat(context.getBean(ElasticsearchRestTemplate.class)).isNotNull();
}
@Test // DATAES-563
public void restConfigContainsElasticsearchOperationsByNameAndAlias() {
AbstractApplicationContext context = new AnnotationConfigApplicationContext(RestConfig.class);
assertThat(context.getBean("elasticsearchOperations")).isNotNull();
assertThat(context.getBean("elasticsearchTemplate")).isNotNull();
}
@Test // DATAES-504
public void reactiveConfigContainsReactiveElasticsearchTemplate() {
AbstractApplicationContext context = new AnnotationConfigApplicationContext(ReactiveRestConfig.class);
assertThat(context.getBean(ReactiveElasticsearchTemplate.class)).isNotNull();
}
@Configuration
static class StubConfig extends ElasticsearchConfigurationSupport {
}
@Configuration
static class ReactiveRestConfig extends AbstractReactiveElasticsearchConfiguration {
@Override
@Bean
public ReactiveElasticsearchClient reactiveElasticsearchClient() {
return mock(ReactiveElasticsearchClient.class);
}
}
@Configuration
static class RestConfig extends AbstractElasticsearchConfiguration {
@Override
public RestHighLevelClient elasticsearchClient() {
return mock(RestHighLevelClient.class);
}
}
@Configuration
static class EntityMapperConfig extends ElasticsearchConfigurationSupport {}

View File

@ -1,76 +0,0 @@
/*
* Copyright 2019-2023 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.configuration;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.Mockito.*;
import org.elasticsearch.client.RestHighLevelClient;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.client.erhlc.AbstractElasticsearchConfiguration;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import org.springframework.data.elasticsearch.repository.config.EnableElasticsearchRepositories;
import org.springframework.lang.Nullable;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
/**
* Tests for {@link AbstractElasticsearchConfiguration}.
*
* @author Peter-Josef Meisch
*/
@ExtendWith(SpringExtension.class)
@ContextConfiguration
public class ElasticsearchConfigurationERHLCTests {
/*
* using a repository with an entity that is set to createIndex = false as we have no elastic running for this test
* and just check that all the necessary beans are created.
*/
@Autowired private CreateIndexFalseRepository repository;
@Configuration
@EnableElasticsearchRepositories(basePackages = { "org.springframework.data.elasticsearch.config.configuration" },
considerNestedRepositories = true)
static class Config extends AbstractElasticsearchConfiguration {
@Override
public RestHighLevelClient elasticsearchClient() {
return mock(RestHighLevelClient.class);
}
}
@Test // DATAES-563
public void bootstrapsRepository() {
assertThat(repository).isNotNull();
}
@Document(indexName = "test-index-config-configuration", createIndex = false)
static class CreateIndexFalseEntity {
@Nullable
@Id private String id;
}
interface CreateIndexFalseRepository extends ElasticsearchRepository<CreateIndexFalseEntity, String> {}
}

View File

@ -26,7 +26,6 @@ import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.client.ClientConfiguration;
import org.springframework.data.elasticsearch.client.elc.ReactiveElasticsearchClient;
import org.springframework.data.elasticsearch.client.elc.ReactiveElasticsearchConfiguration;
import org.springframework.data.elasticsearch.client.erhlc.AbstractElasticsearchConfiguration;
import org.springframework.data.elasticsearch.core.ReactiveElasticsearchOperations;
import org.springframework.data.elasticsearch.repository.ReactiveElasticsearchRepository;
import org.springframework.data.elasticsearch.repository.config.EnableReactiveElasticsearchRepositories;
@ -36,8 +35,6 @@ import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
/**
* Tests for {@link AbstractElasticsearchConfiguration}.
*
* @author Peter-Josef Meisch
* @since 4.4
*/

View File

@ -1,78 +0,0 @@
/*
* Copyright 2019-2023 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.configuration;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.Mockito.*;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.client.erhlc.AbstractElasticsearchConfiguration;
import org.springframework.data.elasticsearch.client.erhlc.AbstractReactiveElasticsearchConfiguration;
import org.springframework.data.elasticsearch.client.erhlc.ReactiveElasticsearchClient;
import org.springframework.data.elasticsearch.repository.ReactiveElasticsearchRepository;
import org.springframework.data.elasticsearch.repository.config.EnableReactiveElasticsearchRepositories;
import org.springframework.lang.Nullable;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
/**
* Tests for {@link AbstractElasticsearchConfiguration}.
*
* @author Peter-Josef Meisch
* @since 4.4
*/
@ExtendWith(SpringExtension.class)
@ContextConfiguration
public class ReactiveElasticsearchConfigurationERHLCTests {
/*
* using a repository with an entity that is set to createIndex = false as we have no elastic running for this test
* and just check that all the necessary beans are created.
*/
@Autowired private CreateIndexFalseRepository repository;
@Configuration
@EnableReactiveElasticsearchRepositories(
basePackages = { "org.springframework.data.elasticsearch.config.configuration" },
considerNestedRepositories = true)
static class Config extends AbstractReactiveElasticsearchConfiguration {
@Override
public ReactiveElasticsearchClient reactiveElasticsearchClient() {
return mock(ReactiveElasticsearchClient.class);
}
}
@Test
public void bootstrapsRepository() {
assertThat(repository).isNotNull();
}
@Document(indexName = "test-index-config-configuration", createIndex = false)
static class CreateIndexFalseEntity {
@Nullable
@Id private String id;
}
interface CreateIndexFalseRepository extends ReactiveElasticsearchRepository<CreateIndexFalseEntity, String> {}
}

View File

@ -1,44 +0,0 @@
/*
* Copyright 2019-2023 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.nested;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.elasticsearch.junit.jupiter.ElasticsearchRestTemplateConfiguration;
import org.springframework.data.elasticsearch.repository.config.EnableElasticsearchRepositories;
import org.springframework.data.elasticsearch.utils.IndexNameProvider;
import org.springframework.test.context.ContextConfiguration;
/**
* @author Peter-Josef Meisch
*/
@ContextConfiguration(classes = { EnableNestedRepositoriesERHLCIntegrationTests.Config.class })
public class EnableNestedRepositoriesERHLCIntegrationTests extends EnableNestedRepositoriesIntegrationTests {
@Configuration
@Import({ ElasticsearchRestTemplateConfiguration.class })
@EnableElasticsearchRepositories(basePackages = { "org.springframework.data.elasticsearch.config.nested" },
considerNestedRepositories = true)
static class Config {
@Bean
IndexNameProvider indexNameProvider() {
return new IndexNameProvider("nested-repositories-es7");
}
}
}

View File

@ -1,39 +0,0 @@
/*
* Copyright 2020-2023 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 org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.elasticsearch.junit.jupiter.ElasticsearchRestTemplateConfiguration;
import org.springframework.data.elasticsearch.repository.config.EnableElasticsearchRepositories;
import org.springframework.data.elasticsearch.utils.IndexNameProvider;
/**
* @author Peter-Josef Meisch
*/
public class EnableRepositoriesERHLCIntegrationTests extends EnableRepositoriesIntegrationTests {
@Configuration
@Import({ ElasticsearchRestTemplateConfiguration.class })
@EnableElasticsearchRepositories
static class Config {
@Bean
IndexNameProvider indexNameProvider() {
return new IndexNameProvider("repositories-es7");
}
}
}

View File

@ -15,15 +15,15 @@
*/
package org.springframework.data.elasticsearch.core;
import static java.util.Collections.*;
import static java.util.Collections.singletonList;
import static org.assertj.core.api.Assertions.*;
import static org.elasticsearch.index.query.QueryBuilders.*;
import static org.springframework.data.elasticsearch.annotations.Document.VersionType.*;
import static org.springframework.data.elasticsearch.annotations.Document.VersionType.EXTERNAL_GTE;
import static org.springframework.data.elasticsearch.annotations.FieldType.*;
import static org.springframework.data.elasticsearch.annotations.FieldType.Integer;
import static org.springframework.data.elasticsearch.core.document.Document.*;
import static org.springframework.data.elasticsearch.utils.IdGenerator.*;
import static org.springframework.data.elasticsearch.utils.IndexBuilder.*;
import static org.springframework.data.elasticsearch.core.document.Document.create;
import static org.springframework.data.elasticsearch.core.query.StringQuery.MATCH_ALL;
import static org.springframework.data.elasticsearch.utils.IdGenerator.nextIdAsString;
import static org.springframework.data.elasticsearch.utils.IndexBuilder.buildIndex;
import java.lang.Double;
import java.lang.Integer;
@ -34,7 +34,6 @@ import java.util.stream.Collectors;
import java.util.stream.IntStream;
import org.assertj.core.api.SoftAssertions;
import org.assertj.core.util.Lists;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Order;
@ -52,7 +51,6 @@ import org.springframework.data.domain.Sort;
import org.springframework.data.elasticsearch.annotations.*;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.ScriptedField;
import org.springframework.data.elasticsearch.client.erhlc.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.document.Explanation;
import org.springframework.data.elasticsearch.core.geo.GeoPoint;
import org.springframework.data.elasticsearch.core.index.AliasAction;
@ -369,8 +367,8 @@ public abstract class ElasticsearchIntegrationTests {
operations.bulkIndex(indexQueries, IndexCoordinates.of(indexNameProvider.indexName()));
// when
Query query = new NativeSearchQueryBuilder().withIds(Arrays.asList(documentId, documentId2))
.withFields("message", "type").build();
Query query = operations.queryBuilderWithIds(Arrays.asList(documentId, documentId2)).withFields("message", "type")
.build();
List<MultiGetItem<SampleEntity>> sampleEntities = operations.multiGet(query, SampleEntity.class,
IndexCoordinates.of(indexNameProvider.indexName()));
@ -431,7 +429,7 @@ public abstract class ElasticsearchIntegrationTests {
assertThatThrownBy(
() -> operations.search(query, SampleEntity.class, IndexCoordinates.of(indexNameProvider.indexName())))
.isInstanceOf(Exception.class);
.isInstanceOf(Exception.class);
}
@Test // DATAES-422 - Add support for IndicesOptions in search queries
@ -877,7 +875,7 @@ public abstract class ElasticsearchIntegrationTests {
operations.index(indexQuery, IndexCoordinates.of(indexNameProvider.indexName()));
StringQuery stringQuery = new StringQuery(matchAllQuery().toString());
StringQuery stringQuery = new StringQuery(MATCH_ALL);
// when
SearchHits<SampleEntity> searchHits = operations.search(stringQuery, SampleEntity.class,
@ -929,7 +927,7 @@ public abstract class ElasticsearchIntegrationTests {
operations.index(indexQuery, IndexCoordinates.of(indexNameProvider.indexName()));
StringQuery stringQuery = new StringQuery(matchAllQuery().toString(), PageRequest.of(0, 10));
StringQuery stringQuery = new StringQuery(MATCH_ALL, PageRequest.of(0, 10));
// when
SearchHits<SampleEntity> searchHits = operations.search(stringQuery, SampleEntity.class,
@ -955,8 +953,7 @@ public abstract class ElasticsearchIntegrationTests {
operations.index(indexQuery, IndexCoordinates.of(indexNameProvider.indexName()));
StringQuery stringQuery = new StringQuery(matchAllQuery().toString(), PageRequest.of(0, 10),
Sort.by(Sort.Order.asc("message")));
StringQuery stringQuery = new StringQuery(MATCH_ALL, PageRequest.of(0, 10), Sort.by(Sort.Order.asc("message")));
// when
SearchHits<SampleEntity> searchHits = operations.search(stringQuery, SampleEntity.class,
@ -978,7 +975,7 @@ public abstract class ElasticsearchIntegrationTests {
operations.index(indexQuery, IndexCoordinates.of(indexNameProvider.indexName()));
StringQuery stringQuery = new StringQuery(termQuery("id", documentId).toString());
StringQuery stringQuery = new StringQuery(" { \"term\": { \"id\": " + documentId + "}}");
// when
SearchHit<SampleEntity> sampleEntity1 = operations.searchOne(stringQuery, SampleEntity.class,
@ -1041,7 +1038,7 @@ public abstract class ElasticsearchIntegrationTests {
operations.delete(criteriaQuery, SampleEntity.class, IndexCoordinates.of(indexNameProvider.indexName()));
// then
StringQuery stringQuery = new StringQuery(matchAllQuery().toString());
StringQuery stringQuery = new StringQuery(MATCH_ALL);
SearchHits<SampleEntity> sampleEntities = operations.search(stringQuery, SampleEntity.class,
IndexCoordinates.of(indexNameProvider.indexName()));
@ -1482,7 +1479,7 @@ public abstract class ElasticsearchIntegrationTests {
// when
operations.bulkIndex(indexQueries, IndexCoordinates.of(indexNameProvider.indexName()));
StringQuery stringQuery = new StringQuery(matchAllQuery().toString());
StringQuery stringQuery = new StringQuery(MATCH_ALL);
SearchHits<SampleEntity> sampleEntities = operations.search(stringQuery, SampleEntity.class,
IndexCoordinates.of(indexNameProvider.indexName()));
@ -1544,19 +1541,14 @@ public abstract class ElasticsearchIntegrationTests {
assertThat(mappingFromAlias).isNotNull();
assertThat(
((Map<String, Object>) ((Map<String, Object>) mappingFromAlias.get("properties")).get("message")).get("type"))
.isEqualTo("text");
.isEqualTo("text");
}
@Test
public void shouldDeleteIndexForGivenEntity() {
// given
Class<?> clazz = SampleEntity.class;
// when
indexOperations.delete();
// then
assertThat(indexOperations.exists()).isFalse();
}
@ -1740,8 +1732,7 @@ public abstract class ElasticsearchIntegrationTests {
queries.add(getTermQuery("message", "ab"));
queries.add(getTermQuery("description", "bc"));
List<SearchHits<?>> searchHitsList = operations.multiSearch(queries,
Lists.newArrayList(SampleEntity.class, Book.class),
List<SearchHits<?>> searchHitsList = operations.multiSearch(queries, List.of(SampleEntity.class, Book.class),
IndexCoordinates.of(indexNameProvider.indexName(), bookIndex.getIndexName()));
bookIndexOperations.delete();
@ -1773,8 +1764,7 @@ public abstract class ElasticsearchIntegrationTests {
queries.add(getTermQuery("message", "ab"));
queries.add(getTermQuery("description", "bc"));
List<SearchHits<?>> searchHitsList = operations.multiSearch(queries,
Lists.newArrayList(SampleEntity.class, Book.class),
List<SearchHits<?>> searchHitsList = operations.multiSearch(queries, List.of(SampleEntity.class, Book.class),
List.of(IndexCoordinates.of(indexNameProvider.indexName()), IndexCoordinates.of(bookIndex.getIndexName())));
bookIndexOperations.delete();
@ -2343,7 +2333,7 @@ public abstract class ElasticsearchIntegrationTests {
}"""; //
indexOperations.delete();
indexOperations.create(parse(settings));
indexOperations.create(org.springframework.data.elasticsearch.core.document.Document.parse(settings));
Settings storedSettings = indexOperations.getSettings().flatten();
assertThat(indexOperations.exists()).isTrue();
@ -2386,7 +2376,7 @@ public abstract class ElasticsearchIntegrationTests {
}"""; //
indexOperations.delete();
indexOperations.create(parse(settings));
indexOperations.create(org.springframework.data.elasticsearch.core.document.Document.parse(settings));
indexOperations.putMapping(SampleEntity.class);
Settings storedSettings = indexOperations.getSettings().flatten();
@ -2606,7 +2596,7 @@ public abstract class ElasticsearchIntegrationTests {
assertThat(sampleEntities).hasSize(2);
assertThat(
sampleEntities.stream().map(SearchHit::getContent).map(SampleEntity::getMessage).collect(Collectors.toList()))
.doesNotContain(notFindableMessage);
.doesNotContain(notFindableMessage);
}
@Test // DATAES-525
@ -2642,7 +2632,7 @@ public abstract class ElasticsearchIntegrationTests {
assertThat(sampleEntities).hasSize(2);
assertThat(
sampleEntities.stream().map(SearchHit::getContent).map(SampleEntity::getMessage).collect(Collectors.toList()))
.doesNotContain(notFindableMessage);
.doesNotContain(notFindableMessage);
}
@Test // DATAES-565
@ -2676,7 +2666,7 @@ public abstract class ElasticsearchIntegrationTests {
.doesNotContain((String) null);
assertThat(
sampleEntities.stream().map(SearchHit::getContent).map(SampleEntity::getMessage).collect(Collectors.toList()))
.containsOnly((String) null);
.containsOnly((String) null);
}
@Test // DATAES-457

View File

@ -1,60 +0,0 @@
/*
* Copyright 2020-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.elasticsearch.core;
import static org.elasticsearch.index.query.QueryBuilders.*;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.index.query.InnerHitBuilder;
import org.elasticsearch.index.query.NestedQueryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.elasticsearch.client.erhlc.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.data.elasticsearch.junit.jupiter.ElasticsearchRestTemplateConfiguration;
import org.springframework.data.elasticsearch.utils.IndexNameProvider;
import org.springframework.test.context.ContextConfiguration;
/**
* @author Peter-Josef Meisch
*/
@ContextConfiguration(classes = { InnerHitsERHLCIntegrationTests.Config.class })
public class InnerHitsERHLCIntegrationTests extends InnerHitsIntegrationTests {
@Configuration
@Import({ ElasticsearchRestTemplateConfiguration.class })
static class Config {
@Bean
IndexNameProvider indexNameProvider() {
return new IndexNameProvider("innerhits-es7");
}
}
@Override
protected Query buildQueryForInnerHits(String innerHitName, String nestedQueryPath, String matchField,
String matchValue) {
NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
NestedQueryBuilder nestedQueryBuilder = nestedQuery(nestedQueryPath, matchQuery(matchField, matchValue),
ScoreMode.Avg);
nestedQueryBuilder.innerHit(new InnerHitBuilder(innerHitName));
queryBuilder.withQuery(nestedQueryBuilder);
return queryBuilder.build();
}
}

View File

@ -1,53 +0,0 @@
/*
* Copyright 2019-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.elasticsearch.core;
import static org.elasticsearch.index.query.QueryBuilders.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.elasticsearch.client.erhlc.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.data.elasticsearch.junit.jupiter.ElasticsearchRestTemplateConfiguration;
import org.springframework.data.elasticsearch.utils.IndexNameProvider;
import org.springframework.test.context.ContextConfiguration;
/**
* @author Peter-Josef Meisch
*/
@ContextConfiguration(classes = { LogEntityERHLCIntegrationTests.Config.class })
public class LogEntityERHLCIntegrationTests extends LogEntityIntegrationTests {
@Configuration
@Import({ ElasticsearchRestTemplateConfiguration.class })
static class Config {
@Bean
IndexNameProvider indexNameProvider() {
return new IndexNameProvider("logentity-es7");
}
}
@Override
Query termQueryForIp(String ip) {
return new NativeSearchQueryBuilder().withQuery(termQuery("ip", ip)).build();
}
@Override
Query rangeQueryForIp(String from, String to) {
return new NativeSearchQueryBuilder().withQuery(rangeQuery("ip").from("10.10.10.1").to("10.10.10.3")).build();
}
}

View File

@ -1,45 +0,0 @@
/*
* Copyright 2022-2023 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 org.junit.jupiter.api.Disabled;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.elasticsearch.junit.jupiter.ElasticsearchRestTemplateConfiguration;
import org.springframework.data.elasticsearch.utils.IndexNameProvider;
import org.springframework.test.context.ContextConfiguration;
/**
* This test class is disabled on purpose. PIT will be introduced in Spring Data Elasticsearch 5.0 where the old
* RestHighLevelClient and the {@link org.springframework.data.elasticsearch.client.erhlc.ElasticsearchRestTemplate} are
* deprecated. We therefore do not add new features to this implementation anymore.
*
* @author Peter-Josef Meisch
*/
@Disabled
@ContextConfiguration(classes = { PointInTimeERHLCIntegrationTests.Config.class })
public class PointInTimeERHLCIntegrationTests extends PointInTimeIntegrationTests {
@Configuration
@Import({ ElasticsearchRestTemplateConfiguration.class })
static class Config {
@Bean
IndexNameProvider indexNameProvider() {
return new IndexNameProvider("point-in-time-es7");
}
}
}

View File

@ -1,104 +0,0 @@
/*
* Copyright 2022-2023 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.assertj.core.api.Assertions.*;
import static org.elasticsearch.index.query.QueryBuilders.*;
import org.elasticsearch.index.query.InnerHitBuilder;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.collapse.CollapseBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.elasticsearch.client.erhlc.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.BaseQueryBuilder;
import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.data.elasticsearch.junit.jupiter.ReactiveElasticsearchRestTemplateConfiguration;
import org.springframework.data.elasticsearch.utils.IndexNameProvider;
import org.springframework.lang.Nullable;
import org.springframework.test.context.ContextConfiguration;
/**
* @author Peter-Josef Meisch
* @since 4.4
*/
@ContextConfiguration(classes = ReactiveElasticsearchERHLCIntegrationTests.Config.class)
public class ReactiveElasticsearchERHLCIntegrationTests extends ReactiveElasticsearchIntegrationTests {
@Configuration
@Import({ ReactiveElasticsearchRestTemplateConfiguration.class })
static class Config {
@Bean
IndexNameProvider indexNameProvider() {
return new IndexNameProvider("reactive-template-es7");
}
}
@Override
protected Query getTermsAggsQuery(String aggsName, String aggsField) {
return new NativeSearchQueryBuilder().withQuery(matchAllQuery())
.addAggregation(AggregationBuilders.terms("messages").field("message")).build();
}
@Override
protected BaseQueryBuilder<?, ?> getBuilderWithMatchAllQuery() {
return new NativeSearchQueryBuilder().withQuery(matchAllQuery());
}
@Override
protected BaseQueryBuilder<?, ?> getBuilderWithTermQuery(String field, String value) {
return new NativeSearchQueryBuilder().withQuery(termQuery(field, value));
}
@Override
protected Query getQueryWithCollapse(String collapseField, @Nullable String innerHits, @Nullable Integer size) {
CollapseBuilder collapseBuilder = new CollapseBuilder(collapseField);
if (innerHits != null) {
InnerHitBuilder innerHitBuilder = new InnerHitBuilder(innerHits);
if (size != null) {
innerHitBuilder.setSize(size);
}
collapseBuilder.setInnerHits(innerHitBuilder);
}
return new NativeSearchQueryBuilder().withQuery(matchAllQuery()).withCollapseBuilder(collapseBuilder).build();
}
@Override
protected Query queryWithIds(String... ids) {
return new NativeSearchQueryBuilder().withIds(ids).build();
}
@Override
protected <A extends AggregationContainer<?>> void assertThatAggregationsAreCorrect(A aggregationContainer) {
Aggregation aggregation = (Aggregation) aggregationContainer.aggregation();
assertThat(aggregation.getName()).isEqualTo("messages");
assertThat(aggregation instanceof ParsedStringTerms);
ParsedStringTerms parsedStringTerms = (ParsedStringTerms) aggregation;
assertThat(parsedStringTerms.getBuckets().size()).isEqualTo(3);
assertThat(parsedStringTerms.getBucketByKey("message").getDocCount()).isEqualTo(3);
assertThat(parsedStringTerms.getBucketByKey("some").getDocCount()).isEqualTo(2);
assertThat(parsedStringTerms.getBucketByKey("other").getDocCount()).isEqualTo(1);
}
}

View File

@ -17,8 +17,8 @@ package org.springframework.data.elasticsearch.core;
import static java.util.Collections.*;
import static org.assertj.core.api.Assertions.*;
import static org.elasticsearch.index.query.QueryBuilders.*;
import static org.springframework.data.elasticsearch.annotations.FieldType.*;
import static org.springframework.data.elasticsearch.core.query.StringQuery.MATCH_ALL;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@ -65,9 +65,6 @@ import org.springframework.data.elasticsearch.annotations.IndexedIndexName;
import org.springframework.data.elasticsearch.annotations.Mapping;
import org.springframework.data.elasticsearch.annotations.Setting;
import org.springframework.data.elasticsearch.annotations.WriteOnlyProperty;
import org.springframework.data.elasticsearch.client.erhlc.NativeSearchQuery;
import org.springframework.data.elasticsearch.client.erhlc.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.client.erhlc.ReactiveElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.document.Explanation;
import org.springframework.data.elasticsearch.core.index.AliasAction;
import org.springframework.data.elasticsearch.core.index.AliasActionParameters;
@ -81,8 +78,6 @@ import org.springframework.lang.Nullable;
import org.springframework.util.StringUtils;
/**
* Integration tests for {@link ReactiveElasticsearchTemplate}.
*
* @author Christoph Strobl
* @author Mark Paluch
* @author Peter-Josef Meisch
@ -359,7 +354,7 @@ public abstract class ReactiveElasticsearchIntegrationTests {
index(randomEntity("test message"), randomEntity("test test"), randomEntity("some message"));
operations.search(new StringQuery(matchAllQuery().toString()), SampleEntity.class) //
operations.search(new StringQuery(MATCH_ALL), SampleEntity.class) //
.as(StepVerifier::create) //
.expectNextCount(3) //
.verifyComplete();
@ -748,9 +743,7 @@ public abstract class ReactiveElasticsearchIntegrationTests {
entity2.rate = 2;
index(entity2);
NativeSearchQuery query = new NativeSearchQueryBuilder() //
.withIds(Arrays.asList(entity1.getId(), entity2.getId())) //
.build();
var query = operations.queryBuilderWithIds(List.of(entity1.getId(), entity2.getId())).build();
operations.multiGet(query, SampleEntity.class, IndexCoordinates.of(indexNameProvider.indexName())) //
.map(MultiGetItem::getItem).as(StepVerifier::create) //
@ -767,8 +760,7 @@ public abstract class ReactiveElasticsearchIntegrationTests {
entity2.rate = 2;
index(entity2);
NativeSearchQuery query = new NativeSearchQueryBuilder() //
.withIds(Arrays.asList(entity1.getId(), entity2.getId())) //
var query = operations.queryBuilderWithIds(List.of(entity1.getId(), entity2.getId())) //
.withFields("message") //
.build();
@ -804,10 +796,8 @@ public abstract class ReactiveElasticsearchIntegrationTests {
List<UpdateQuery> queries = Arrays.asList(updateQuery1, updateQuery2);
operations.bulkUpdate(queries, IndexCoordinates.of(indexNameProvider.indexName())).block();
NativeSearchQuery getQuery = new NativeSearchQueryBuilder() //
.withIds(Arrays.asList(entity1.getId(), entity2.getId())) //
.build();
operations.multiGet(getQuery, SampleEntity.class, IndexCoordinates.of(indexNameProvider.indexName())) //
var query = operations.queryBuilderWithIds(List.of(entity1.getId(), entity2.getId())).build();
operations.multiGet(query, SampleEntity.class, IndexCoordinates.of(indexNameProvider.indexName())) //
.map(MultiGetItem::getItem) //
.as(StepVerifier::create) //
.expectNextMatches(entity -> entity.getMessage().equals("updated 1")) //
@ -873,7 +863,7 @@ public abstract class ReactiveElasticsearchIntegrationTests {
}
private Query multiGetQueryForOne(String id) {
return new NativeSearchQueryBuilder().withIds(singletonList(id)).build();
return operations.queryBuilderWithIds(List.of(id)).build();
}
@Test // DATAES-799

View File

@ -1,507 +0,0 @@
/*
* Copyright 2018-2023 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.assertj.core.api.Assertions.*;
import static org.mockito.Mockito.*;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import org.apache.lucene.search.TotalHits;
import org.elasticsearch.Version;
import org.elasticsearch.action.DocWriteResponse;
import org.elasticsearch.action.bulk.BulkItemResponse;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.get.MultiGetItemResponse;
import org.elasticsearch.action.get.MultiGetRequest;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.main.MainResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.cluster.ClusterName;
import org.elasticsearch.common.bytes.BytesArray;
import org.elasticsearch.index.get.GetResult;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.Spy;
import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.junit.jupiter.MockitoSettings;
import org.mockito.quality.Strictness;
import org.springframework.data.annotation.Id;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.client.erhlc.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.client.erhlc.ReactiveElasticsearchClient;
import org.springframework.data.elasticsearch.client.erhlc.ReactiveElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.document.Document;
import org.springframework.data.elasticsearch.core.event.ReactiveAfterConvertCallback;
import org.springframework.data.elasticsearch.core.event.ReactiveAfterSaveCallback;
import org.springframework.data.elasticsearch.core.event.ReactiveBeforeConvertCallback;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.data.mapping.callback.ReactiveEntityCallbacks;
import org.springframework.lang.Nullable;
import org.springframework.util.CollectionUtils;
/**
* @author Roman Puchkovskiy
* @author Peter-Josef Meisch
*/
@ExtendWith(MockitoExtension.class)
@MockitoSettings(strictness = Strictness.LENIENT)
public class ReactiveElasticsearchTemplateCallbackTests {
private ReactiveElasticsearchTemplate template;
@Mock private ReactiveElasticsearchClient client;
@Mock private IndexResponse indexResponse;
@Mock private BulkResponse bulkResponse;
@Mock private BulkItemResponse bulkItemResponse;
@Mock private DocWriteResponse docWriteResponse;
@Mock private GetResult getResult;
@Mock private GetResponse getResponse;
@Mock private MultiGetItemResponse multiGetItemResponse;
@Mock private org.elasticsearch.search.SearchHit searchHit;
@Mock private org.elasticsearch.action.search.SearchResponse searchResponse;
private final IndexCoordinates index = IndexCoordinates.of("index");
@Spy private ValueCapturingAfterSaveCallback afterSaveCallback = new ValueCapturingAfterSaveCallback();
@Spy private ValueCapturingAfterConvertCallback afterConvertCallback = new ValueCapturingAfterConvertCallback();
@Spy private ValueCapturingBeforeConvertCallback beforeConvertCallback = new ValueCapturingBeforeConvertCallback();
@BeforeEach
public void setUp() {
when(client.info()).thenReturn(
Mono.just(new MainResponse("mockNodename", Version.CURRENT, new ClusterName("mockCluster"), "mockUuid", null)));
template = new ReactiveElasticsearchTemplate(client);
when(client.index(any(IndexRequest.class))).thenReturn(Mono.just(indexResponse));
doReturn("response-id").when(indexResponse).getId();
when(client.bulk(any(BulkRequest.class))).thenReturn(Mono.just(bulkResponse));
doReturn(new BulkItemResponse[] { bulkItemResponse, bulkItemResponse }).when(bulkResponse).getItems();
doReturn(docWriteResponse).when(bulkItemResponse).getResponse();
doReturn("response-id").when(docWriteResponse).getId();
doReturn(true).when(getResult).isExists();
doReturn(false).when(getResult).isSourceEmpty();
doReturn(new HashMap<String, Object>() {
{
put("id", "init");
put("firstname", "luke");
}
}).when(getResult).getSource();
doReturn(true).when(getResponse).isExists();
doReturn(new HashMap<String, Object>() {
{
put("id", "init");
put("firstname", "luke");
}
}).when(getResponse).getSourceAsMap();
doReturn(getResponse).when(multiGetItemResponse).getResponse();
when(client.multiGet(any(MultiGetRequest.class))).thenReturn(Flux.just(multiGetItemResponse, multiGetItemResponse));
doReturn(Mono.just(getResult)).when(client).get(any(GetRequest.class));
when(client.search(any(SearchRequest.class))).thenReturn(Flux.just(searchHit, searchHit));
when(client.searchForResponse(any(SearchRequest.class))).thenReturn(Mono.just(searchResponse));
when(searchResponse.getHits()).thenReturn(
new org.elasticsearch.search.SearchHits(new org.elasticsearch.search.SearchHit[] { searchHit, searchHit },
new TotalHits(2, TotalHits.Relation.EQUAL_TO), 1.0f));
doReturn(new BytesArray(new byte[8])).when(searchHit).getSourceRef();
doReturn(new HashMap<String, Object>() {
{
put("id", "init");
put("firstname", "luke");
}
}).when(searchHit).getSourceAsMap();
when(client.scroll(any(SearchRequest.class))).thenReturn(Flux.just(searchHit, searchHit));
}
@Test // DATAES-771
void saveOneShouldInvokeAfterSaveCallbacks() {
template.setEntityCallbacks(ReactiveEntityCallbacks.create(afterSaveCallback));
Person entity = new Person("init", "luke");
Person saved = template.save(entity).block(Duration.ofSeconds(1));
verify(afterSaveCallback).onAfterSave(eq(entity), any());
assertThat(saved.firstname).isEqualTo("after-save");
}
@Test // DATAES-771
void saveOneFromPublisherShouldInvokeAfterSaveCallbacks() {
template.setEntityCallbacks(ReactiveEntityCallbacks.create(afterSaveCallback));
Person entity = new Person("init", "luke");
Person saved = template.save(Mono.just(entity)).block(Duration.ofSeconds(1));
verify(afterSaveCallback).onAfterSave(eq(entity), any());
assertThat(saved.firstname).isEqualTo("after-save");
}
@Test // DATAES-771
void saveWithIndexCoordinatesShouldInvokeAfterSaveCallbacks() {
template.setEntityCallbacks(ReactiveEntityCallbacks.create(afterSaveCallback));
Person entity = new Person("init", "luke");
Person saved = template.save(entity, index).block(Duration.ofSeconds(1));
verify(afterSaveCallback).onAfterSave(eq(entity), eq(index));
assertThat(saved.firstname).isEqualTo("after-save");
}
@Test // DATAES-771
void saveFromPublisherWithIndexCoordinatesShouldInvokeAfterSaveCallbacks() {
template.setEntityCallbacks(ReactiveEntityCallbacks.create(afterSaveCallback));
Person entity = new Person("init", "luke");
Person saved = template.save(Mono.just(entity), index).block(Duration.ofSeconds(1));
verify(afterSaveCallback).onAfterSave(eq(entity), eq(index));
assertThat(saved.firstname).isEqualTo("after-save");
}
@Test // DATAES-771
void saveAllShouldInvokeAfterSaveCallbacks() {
template.setEntityCallbacks(ReactiveEntityCallbacks.create(afterSaveCallback));
Person entity1 = new Person("init1", "luke1");
Person entity2 = new Person("init2", "luke2");
List<Person> saved = template.saveAll(Arrays.asList(entity1, entity2), index).toStream()
.collect(Collectors.toList());
verify(afterSaveCallback, times(2)).onAfterSave(any(), eq(index));
assertThat(saved.get(0).firstname).isEqualTo("after-save");
assertThat(saved.get(1).firstname).isEqualTo("after-save");
}
@Test // DATAES-771
void saveFromMonoAllShouldInvokeAfterSaveCallbacks() {
template.setEntityCallbacks(ReactiveEntityCallbacks.create(afterSaveCallback));
Person entity1 = new Person("init1", "luke1");
Person entity2 = new Person("init2", "luke2");
List<Person> saved = template.saveAll(Mono.just(Arrays.asList(entity1, entity2)), index).toStream()
.collect(Collectors.toList());
verify(afterSaveCallback, times(2)).onAfterSave(any(), eq(index));
assertThat(saved.get(0).firstname).isEqualTo("after-save");
assertThat(saved.get(1).firstname).isEqualTo("after-save");
}
@Test // DATAES-772, #1678
void multiGetShouldInvokeAfterConvertCallbacks() {
template.setEntityCallbacks(ReactiveEntityCallbacks.create(afterConvertCallback));
List<MultiGetItem<Person>> results = template.multiGet(pagedQueryForTwo(), Person.class, index)
.timeout(Duration.ofSeconds(1)).toStream().collect(Collectors.toList());
verify(afterConvertCallback, times(2)).onAfterConvert(eq(new Person("init", "luke")), eq(lukeDocument()),
eq(index));
assertThat(results.get(0).getItem().firstname).isEqualTo("after-convert");
assertThat(results.get(1).getItem().firstname).isEqualTo("after-convert");
}
@Test // DATAES-772
void getShouldInvokeAfterConvertCallbacks() {
template.setEntityCallbacks(ReactiveEntityCallbacks.create(afterConvertCallback));
Person result = template.get("init", Person.class).block(Duration.ofSeconds(1));
verify(afterConvertCallback).onAfterConvert(eq(new Person("init", "luke")), eq(lukeDocument()), any());
assertThat(result.firstname).isEqualTo("after-convert");
}
@Test // DATAES-772
void getWithIndexCoordinatesShouldInvokeAfterConvertCallbacks() {
template.setEntityCallbacks(ReactiveEntityCallbacks.create(afterConvertCallback));
Person result = template.get("init", Person.class, index).block(Duration.ofSeconds(1));
verify(afterConvertCallback).onAfterConvert(eq(new Person("init", "luke")), eq(lukeDocument()), eq(index));
assertThat(result.firstname).isEqualTo("after-convert");
}
private Query pagedQueryForTwo() {
return new NativeSearchQueryBuilder().withIds(Arrays.asList("init1", "init2")).withPageable(PageRequest.of(0, 10))
.build();
}
private Document lukeDocument() {
return Document.create().append("id", "init").append("firstname", "luke");
}
@Test // DATAES-772
void searchShouldInvokeAfterConvertCallbacks() {
template.setEntityCallbacks(ReactiveEntityCallbacks.create(afterConvertCallback));
List<SearchHit<Person>> results = template.search(pagedQueryForTwo(), Person.class).timeout(Duration.ofSeconds(1))
.toStream().collect(Collectors.toList());
verify(afterConvertCallback, times(2)).onAfterConvert(eq(new Person("init", "luke")), eq(lukeDocument()), any());
assertThat(results.get(0).getContent().firstname).isEqualTo("after-convert");
assertThat(results.get(1).getContent().firstname).isEqualTo("after-convert");
}
@Test // DATAES-796
void searchForPageShouldInvokeAfterConvertCallbacks() {
template.setEntityCallbacks(ReactiveEntityCallbacks.create(afterConvertCallback));
SearchPage<Person> searchPage = template.searchForPage(pagedQueryForTwo(), Person.class)
.timeout(Duration.ofSeconds(1)).block();
verify(afterConvertCallback, times(2)).onAfterConvert(eq(new Person("init", "luke")), eq(lukeDocument()), any());
SearchHits<Person> searchHits = searchPage.getSearchHits();
assertThat(searchHits.getSearchHit(0).getContent().firstname).isEqualTo("after-convert");
assertThat(searchHits.getSearchHit(1).getContent().firstname).isEqualTo("after-convert");
}
@Test // DATAES-772
void searchWithIndexCoordinatesShouldInvokeAfterConvertCallbacks() {
template.setEntityCallbacks(ReactiveEntityCallbacks.create(afterConvertCallback));
List<SearchHit<Person>> results = template.search(pagedQueryForTwo(), Person.class, index)
.timeout(Duration.ofSeconds(1)).toStream().collect(Collectors.toList());
verify(afterConvertCallback, times(2)).onAfterConvert(eq(new Person("init", "luke")), eq(lukeDocument()),
eq(index));
assertThat(results.get(0).getContent().firstname).isEqualTo("after-convert");
assertThat(results.get(1).getContent().firstname).isEqualTo("after-convert");
}
@Test // DATAES-772
void searchWithResultTypeShouldInvokeAfterConvertCallbacks() {
template.setEntityCallbacks(ReactiveEntityCallbacks.create(afterConvertCallback));
List<SearchHit<Person>> results = template.search(pagedQueryForTwo(), Person.class, Person.class)
.timeout(Duration.ofSeconds(1)).toStream().collect(Collectors.toList());
verify(afterConvertCallback, times(2)).onAfterConvert(eq(new Person("init", "luke")), eq(lukeDocument()), any());
assertThat(results.get(0).getContent().firstname).isEqualTo("after-convert");
assertThat(results.get(1).getContent().firstname).isEqualTo("after-convert");
}
@Test // DATAES-772
void searchWithResultTypeAndIndexCoordinatesShouldInvokeAfterConvertCallbacks() {
template.setEntityCallbacks(ReactiveEntityCallbacks.create(afterConvertCallback));
List<SearchHit<Person>> results = template.search(pagedQueryForTwo(), Person.class, Person.class, index)
.timeout(Duration.ofSeconds(1)).toStream().collect(Collectors.toList());
verify(afterConvertCallback, times(2)).onAfterConvert(eq(new Person("init", "luke")), eq(lukeDocument()),
eq(index));
assertThat(results.get(0).getContent().firstname).isEqualTo("after-convert");
assertThat(results.get(1).getContent().firstname).isEqualTo("after-convert");
}
@Test // DATAES-785
void saveOneShouldInvokeBeforeConvertCallbacks() {
template.setEntityCallbacks(ReactiveEntityCallbacks.create(beforeConvertCallback));
Person entity = new Person("init1", "luke1");
Person saved = template.save(entity, index).block(Duration.ofSeconds(1));
verify(beforeConvertCallback).onBeforeConvert(any(), eq(index));
assertThat(saved.firstname).isEqualTo("before-convert");
}
@Test // DATAES-785
void saveAllShouldInvokeBeforeConvertCallbacks() {
template.setEntityCallbacks(ReactiveEntityCallbacks.create(beforeConvertCallback));
Person entity1 = new Person("init1", "luke1");
Person entity2 = new Person("init2", "luke2");
List<Person> saved = template.saveAll(Arrays.asList(entity1, entity2), index).toStream()
.collect(Collectors.toList());
verify(beforeConvertCallback, times(2)).onBeforeConvert(any(), eq(index));
assertThat(saved.get(0).firstname).isEqualTo("before-convert");
assertThat(saved.get(1).firstname).isEqualTo("before-convert");
}
static class Person {
@Nullable
@Id String id;
@Nullable String firstname;
public Person() {}
public Person(@Nullable String id, @Nullable String firstname) {
this.id = id;
this.firstname = firstname;
}
@Nullable
public String getId() {
return id;
}
public void setId(@Nullable String id) {
this.id = id;
}
@Nullable
public String getFirstname() {
return firstname;
}
public void setFirstname(@Nullable String firstname) {
this.firstname = firstname;
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
Person person = (Person) o;
if (!Objects.equals(id, person.id))
return false;
return Objects.equals(firstname, person.firstname);
}
@Override
public int hashCode() {
int result = id != null ? id.hashCode() : 0;
result = 31 * result + (firstname != null ? firstname.hashCode() : 0);
return result;
}
}
static class ValueCapturingEntityCallback<T> {
private final List<T> values = new ArrayList<>(1);
protected void capture(T value) {
values.add(value);
}
public List<T> getValues() {
return values;
}
@Nullable
public T getValue() {
return CollectionUtils.lastElement(values);
}
}
static class ValueCapturingAfterSaveCallback extends ValueCapturingEntityCallback<Person>
implements ReactiveAfterSaveCallback<Person> {
@Override
public Mono<Person> onAfterSave(Person entity, IndexCoordinates index) {
return Mono.defer(() -> {
capture(entity);
Person newPerson = new Person() {
{
id = entity.id;
firstname = "after-save";
}
};
return Mono.just(newPerson);
});
}
}
static class ValueCapturingAfterConvertCallback extends ValueCapturingEntityCallback<Person>
implements ReactiveAfterConvertCallback<Person> {
@Override
public Mono<Person> onAfterConvert(Person entity, Document document, IndexCoordinates index) {
return Mono.defer(() -> {
capture(entity);
Person newPerson = new Person() {
{
id = entity.id;
firstname = "after-convert";
}
};
return Mono.just(newPerson);
});
}
}
static class ValueCapturingBeforeConvertCallback extends ValueCapturingEntityCallback<Person>
implements ReactiveBeforeConvertCallback<Person> {
@Override
public Mono<Person> onBeforeConvert(Person entity, IndexCoordinates index) {
return Mono.defer(() -> {
capture(entity);
Person newPerson = new Person() {
{
id = entity.id;
firstname = "before-convert";
}
};
return Mono.just(newPerson);
});
}
}
}

View File

@ -1,338 +0,0 @@
/*
* Copyright 2018-2023 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.assertj.core.api.Assertions.*;
import static org.elasticsearch.action.search.SearchRequest.*;
import static org.mockito.Mockito.*;
import static org.springframework.data.elasticsearch.annotations.FieldType.*;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import java.lang.Double;
import java.lang.Long;
import java.lang.Object;
import java.util.Collections;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.support.IndicesOptions;
import org.elasticsearch.action.support.WriteRequest;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.reindex.DeleteByQueryRequest;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
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.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.ScriptedField;
import org.springframework.data.elasticsearch.client.erhlc.ReactiveElasticsearchClient;
import org.springframework.data.elasticsearch.client.erhlc.ReactiveElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.geo.GeoPoint;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.Criteria;
import org.springframework.data.elasticsearch.core.query.CriteriaQuery;
import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.data.elasticsearch.core.query.StringQuery;
import org.springframework.lang.Nullable;
/**
* @author Christoph Strobl
* @author Peter-Josef Meisch
*/
@ExtendWith(MockitoExtension.class)
public class ReactiveElasticsearchTemplateUnitTests {
@Mock ReactiveElasticsearchClient client;
ReactiveElasticsearchTemplate template;
private IndexCoordinates index = IndexCoordinates.of("index");
@BeforeEach
public void setUp() {
template = new ReactiveElasticsearchTemplate(client);
}
@Test // DATAES-504
public void insertShouldUseDefaultRefreshPolicy() {
ArgumentCaptor<IndexRequest> captor = ArgumentCaptor.forClass(IndexRequest.class);
when(client.index(captor.capture())).thenReturn(Mono.empty());
template.save(Collections.singletonMap("key", "value"), index) //
.as(StepVerifier::create) //
.verifyComplete();
assertThat(captor.getValue().getRefreshPolicy()).isEqualTo(WriteRequest.RefreshPolicy.NONE);
}
@Test // DATAES-504
public void insertShouldApplyRefreshPolicy() {
ArgumentCaptor<IndexRequest> captor = ArgumentCaptor.forClass(IndexRequest.class);
when(client.index(captor.capture())).thenReturn(Mono.empty());
template.setRefreshPolicy(RefreshPolicy.WAIT_UNTIL);
template.save(Collections.singletonMap("key", "value"), index) //
.as(StepVerifier::create) //
.verifyComplete();
assertThat(captor.getValue().getRefreshPolicy()).isEqualTo(WriteRequest.RefreshPolicy.WAIT_UNTIL);
}
@Test // DATAES-504, DATAES-518
public void searchShouldFallBackToDefaultIndexOptionsIfNotSet() {
ArgumentCaptor<SearchRequest> captor = ArgumentCaptor.forClass(SearchRequest.class);
when(client.search(captor.capture())).thenReturn(Flux.empty());
template.search(new CriteriaQuery(new Criteria("*")).setPageable(PageRequest.of(0, 10)), SampleEntity.class) //
.as(StepVerifier::create) //
.verifyComplete();
assertThat(captor.getValue().indicesOptions()).isEqualTo(DEFAULT_INDICES_OPTIONS);
}
@Test // DATAES-504, DATAES-518
public void searchShouldApplyIndexOptionsIfSet() {
ArgumentCaptor<SearchRequest> captor = ArgumentCaptor.forClass(SearchRequest.class);
when(client.search(captor.capture())).thenReturn(Flux.empty());
template.setIndicesOptions(IndicesOptions.LENIENT_EXPAND_OPEN);
Query query = new CriteriaQuery(new Criteria("*")).setPageable(PageRequest.of(0, 10));
template.search(query, SampleEntity.class, index) //
.as(StepVerifier::create) //
.verifyComplete();
assertThat(captor.getValue().indicesOptions()).isEqualTo(IndicesOptions.LENIENT_EXPAND_OPEN);
}
@Test // DATAES-504
public void searchShouldApplyPaginationIfSet() {
ArgumentCaptor<SearchRequest> captor = ArgumentCaptor.forClass(SearchRequest.class);
when(client.search(captor.capture())).thenReturn(Flux.empty());
Query query = new CriteriaQuery(new Criteria("*")).setPageable(PageRequest.of(2, 50));
template.search(query, SampleEntity.class, index) //
.as(StepVerifier::create) //
.verifyComplete();
assertThat(captor.getValue().source().from()).isEqualTo(100);
assertThat(captor.getValue().source().size()).isEqualTo(50);
}
@Test // DATAES-504, DATAES-518
public void searchShouldUseScrollIfPaginationNotSet() {
ArgumentCaptor<SearchRequest> captor = ArgumentCaptor.forClass(SearchRequest.class);
when(client.scroll(captor.capture())).thenReturn(Flux.empty());
template.search(new CriteriaQuery(new Criteria("*")).setPageable(Pageable.unpaged()), SampleEntity.class) //
.as(StepVerifier::create) //
.verifyComplete();
verify(client).scroll(any());
}
@Test // DATAES-504
public void deleteShouldUseDefaultRefreshPolicy() {
ArgumentCaptor<DeleteRequest> captor = ArgumentCaptor.forClass(DeleteRequest.class);
when(client.delete(captor.capture())).thenReturn(Mono.empty());
template.delete("id", index) //
.as(StepVerifier::create) //
.verifyComplete();
assertThat(captor.getValue().getRefreshPolicy()).isEqualTo(WriteRequest.RefreshPolicy.NONE);
}
@Test // DATAES-504
public void deleteShouldApplyRefreshPolicy() {
ArgumentCaptor<DeleteRequest> captor = ArgumentCaptor.forClass(DeleteRequest.class);
when(client.delete(captor.capture())).thenReturn(Mono.empty());
template.setRefreshPolicy(RefreshPolicy.WAIT_UNTIL);
template.delete("id", index) //
.as(StepVerifier::create) //
.verifyComplete();
assertThat(captor.getValue().getRefreshPolicy()).isEqualTo(WriteRequest.RefreshPolicy.WAIT_UNTIL);
}
@Test // DATAES-504
public void deleteByShouldUseDefaultRefreshPolicy() {
ArgumentCaptor<DeleteByQueryRequest> captor = ArgumentCaptor.forClass(DeleteByQueryRequest.class);
when(client.deleteBy(captor.capture())).thenReturn(Mono.empty());
template.delete(new StringQuery(QueryBuilders.matchAllQuery().toString()), Object.class, index) //
.as(StepVerifier::create) //
.verifyComplete();
assertThat(captor.getValue().isRefresh()).isFalse();
}
@Test // DATAES-504
public void deleteByShouldApplyRefreshPolicy() {
ArgumentCaptor<DeleteByQueryRequest> captor = ArgumentCaptor.forClass(DeleteByQueryRequest.class);
when(client.deleteBy(captor.capture())).thenReturn(Mono.empty());
template.setRefreshPolicy(RefreshPolicy.IMMEDIATE);
template.delete(new StringQuery(QueryBuilders.matchAllQuery().toString()), Object.class, index) //
.as(StepVerifier::create) //
.verifyComplete();
assertThat(captor.getValue().isRefresh()).isTrue();
}
@Test // DATAES-504
public void deleteByShouldApplyIndicesOptions() {
ArgumentCaptor<DeleteByQueryRequest> captor = ArgumentCaptor.forClass(DeleteByQueryRequest.class);
when(client.deleteBy(captor.capture())).thenReturn(Mono.empty());
template.delete(new StringQuery(QueryBuilders.matchAllQuery().toString()), Object.class, index) //
.as(StepVerifier::create) //
.verifyComplete();
assertThat(captor.getValue().indicesOptions()).isEqualTo(DEFAULT_INDICES_OPTIONS);
}
@Test // DATAES-504
public void deleteByShouldApplyIndicesOptionsIfSet() {
ArgumentCaptor<DeleteByQueryRequest> captor = ArgumentCaptor.forClass(DeleteByQueryRequest.class);
when(client.deleteBy(captor.capture())).thenReturn(Mono.empty());
template.setIndicesOptions(IndicesOptions.LENIENT_EXPAND_OPEN);
template.delete(new StringQuery(QueryBuilders.matchAllQuery().toString()), Object.class, index) //
.as(StepVerifier::create) //
.verifyComplete();
assertThat(captor.getValue().indicesOptions()).isEqualTo(IndicesOptions.LENIENT_EXPAND_OPEN);
}
@Document(indexName = "test-index-sample-core-reactive-template-Unit")
static class SampleEntity {
@Nullable
@Id private String id;
@Nullable
@Field(type = Text, store = true, fielddata = true) private String type;
@Nullable
@Field(type = Text, store = true, fielddata = true) private String message;
@Nullable private int rate;
@Nullable
@ScriptedField private Double scriptedRate;
@Nullable private boolean available;
@Nullable private String highlightedMessage;
@Nullable private GeoPoint location;
@Nullable
@Version private Long version;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public int getRate() {
return rate;
}
public void setRate(int rate) {
this.rate = rate;
}
public java.lang.Double getScriptedRate() {
return scriptedRate;
}
public void setScriptedRate(java.lang.Double scriptedRate) {
this.scriptedRate = scriptedRate;
}
public boolean isAvailable() {
return available;
}
public void setAvailable(boolean available) {
this.available = available;
}
public String getHighlightedMessage() {
return highlightedMessage;
}
public void setHighlightedMessage(String highlightedMessage) {
this.highlightedMessage = highlightedMessage;
}
public GeoPoint getLocation() {
return location;
}
public void setLocation(GeoPoint location) {
this.location = location;
}
public java.lang.Long getVersion() {
return version;
}
public void setVersion(java.lang.Long version) {
this.version = version;
}
}
}

View File

@ -1,47 +0,0 @@
/*
* Copyright 2022-2023 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 org.junit.jupiter.api.Disabled;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.elasticsearch.junit.jupiter.ReactiveElasticsearchRestTemplateConfiguration;
import org.springframework.data.elasticsearch.utils.IndexNameProvider;
import org.springframework.test.context.ContextConfiguration;
/**
* This test class is disabled on purpose. PIT will be introduced in Spring Data Elasticsearch 5.0 where the old
* RestHighLevelClient and the {@link org.springframework.data.elasticsearch.client.erhlc.ElasticsearchRestTemplate} are
* deprecated. We therefore do not add new features to this implementation anymore. Furthermore we cannot copy the
* necessary code for the reactive implementation like we did before, as point in time was introduced in Elasticsearch
* 7.12 after the license change.
*
* @author Peter-Josef Meisch
*/
@Disabled
@ContextConfiguration(classes = ReactivePointInTimeERHLCIntegrationTests.Config.class)
public class ReactivePointInTimeERHLCIntegrationTests extends ReactivePointInTimeIntegrationTests {
@Configuration
@Import({ ReactiveElasticsearchRestTemplateConfiguration.class })
static class Config {
@Bean
IndexNameProvider indexNameProvider() {
return new IndexNameProvider("reactive-point-in-time-es7");
}
}
}

View File

@ -1,39 +0,0 @@
/*
* Copyright 2022-2023 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 org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.elasticsearch.junit.jupiter.ReactiveElasticsearchRestTemplateConfiguration;
import org.springframework.data.elasticsearch.utils.IndexNameProvider;
import org.springframework.test.context.ContextConfiguration;
/**
* @author Peter-Josef Meisch
*/
@ContextConfiguration(classes = { ReactiveReindexERHLCIntegrationTests.Config.class })
public class ReactiveReindexERHLCIntegrationTests extends ReactiveReindexIntegrationTests {
@Configuration
@Import({ ReactiveElasticsearchRestTemplateConfiguration.class })
static class Config {
@Bean
IndexNameProvider indexNameProvider() {
return new IndexNameProvider("reactive-reindex-es7");
}
}
}

View File

@ -1,47 +0,0 @@
/*
* Copyright 2022-2023 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 org.elasticsearch.index.query.QueryBuilders;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.elasticsearch.client.erhlc.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.data.elasticsearch.junit.jupiter.ElasticsearchRestTemplateConfiguration;
import org.springframework.data.elasticsearch.utils.IndexNameProvider;
import org.springframework.test.context.ContextConfiguration;
/**
* @author Peter-Josef Meisch
*/
@ContextConfiguration(classes = { ReindexERHLCIntegrationTests.Config.class })
public class ReindexERHLCIntegrationTests extends ReindexIntegrationTests {
@Configuration
@Import({ ElasticsearchRestTemplateConfiguration.class })
static class Config {
@Bean
IndexNameProvider indexNameProvider() {
return new IndexNameProvider("reindex-es7");
}
}
@Override
protected Query queryForId(String id) {
return new NativeSearchQueryBuilder().withQuery(QueryBuilders.termQuery("_id", id)).build();
}
}

View File

@ -1,39 +0,0 @@
/*
* Copyright 2021-2023 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 org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.elasticsearch.junit.jupiter.ElasticsearchRestTemplateConfiguration;
import org.springframework.data.elasticsearch.utils.IndexNameProvider;
import org.springframework.test.context.ContextConfiguration;
/**
* @author Peter-Josef Meisch
*/
@ContextConfiguration(classes = { RuntimeFieldsERHLCIntegrationTests.Config.class })
public class RuntimeFieldsERHLCIntegrationTests extends RuntimeFieldsIntegrationTests {
@Configuration
@Import({ ElasticsearchRestTemplateConfiguration.class })
static class Config {
@Bean
IndexNameProvider indexNameProvider() {
return new IndexNameProvider("runtime-fields-rest-template-es7");
}
}
}

View File

@ -1,51 +0,0 @@
/*
* Copyright 2022-2023 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 org.elasticsearch.index.query.MultiMatchQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.elasticsearch.client.erhlc.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.data.elasticsearch.junit.jupiter.ElasticsearchRestTemplateConfiguration;
import org.springframework.data.elasticsearch.utils.IndexNameProvider;
import org.springframework.test.context.ContextConfiguration;
/**
* @author Peter-Josef Meisch
* @since 4.4
*/
@ContextConfiguration(classes = { SearchAsYouTypeERHLCIntegrationTests.Config.class })
public class SearchAsYouTypeERHLCIntegrationTests extends SearchAsYouTypeIntegrationTests {
@Configuration
@Import({ ElasticsearchRestTemplateConfiguration.class })
static class Config {
@Bean
IndexNameProvider indexNameProvider() {
return new IndexNameProvider("search-as-you-type-es7");
}
}
@Override
protected Query buildMultiMatchQuery(String text) {
return new NativeSearchQuery(QueryBuilders.multiMatchQuery(text, //
"suggest", "suggest._2gram", "suggest._3gram", "suggest._4gram").type(MultiMatchQueryBuilder.Type.BOOL_PREFIX));
}
}

View File

@ -1,40 +0,0 @@
/*
* Copyright 2021-2023 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 org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.elasticsearch.junit.jupiter.ElasticsearchRestTemplateConfiguration;
import org.springframework.data.elasticsearch.utils.IndexNameProvider;
import org.springframework.test.context.ContextConfiguration;
/**
* @author Peter-Josef Meisch
*/
@ContextConfiguration(classes = { SourceFilterERHLCIntegrationTests.Config.class })
public class SourceFilterERHLCIntegrationTests extends SourceFilterIntegrationTests {
@Configuration
@Import({ ElasticsearchRestTemplateConfiguration.class })
static class Config {
@Bean
IndexNameProvider indexNameProvider() {
return new IndexNameProvider("source-filter-es7");
}
}
}

View File

@ -1,97 +0,0 @@
/*
* Copyright 2019-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.elasticsearch.core.aggregation;
import static org.assertj.core.api.Assertions.*;
import static org.elasticsearch.index.query.QueryBuilders.*;
import static org.elasticsearch.search.aggregations.AggregationBuilders.*;
import static org.elasticsearch.search.aggregations.PipelineAggregatorBuilders.*;
import org.elasticsearch.action.search.SearchType;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.pipeline.ParsedStatsBucket;
import org.elasticsearch.search.aggregations.pipeline.StatsBucket;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.elasticsearch.client.erhlc.ElasticsearchAggregations;
import org.springframework.data.elasticsearch.client.erhlc.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.AggregationsContainer;
import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.data.elasticsearch.junit.jupiter.ElasticsearchRestTemplateConfiguration;
import org.springframework.data.elasticsearch.repository.config.EnableElasticsearchRepositories;
import org.springframework.data.elasticsearch.utils.IndexNameProvider;
import org.springframework.test.context.ContextConfiguration;
/**
* @author Peter-Josef Meisch
*/
@ContextConfiguration(classes = { AggregationERHLCIntegrationTests.Config.class })
public class AggregationERHLCIntegrationTests extends AggregationIntegrationTests {
@Configuration
@Import({ ElasticsearchRestTemplateConfiguration.class })
@EnableElasticsearchRepositories(considerNestedRepositories = true)
static class Config {
@Bean
IndexNameProvider indexNameProvider() {
return new IndexNameProvider("aggs-es7");
}
}
protected Query getTermsAggsQuery(String aggsName, String aggsField) {
return new NativeSearchQueryBuilder() //
.withQuery(matchAllQuery()) //
.withSearchType(SearchType.DEFAULT) //
.withAggregations(terms(aggsName).field(aggsField)) //
.withMaxResults(0) //
.build();
}
protected void assertThatAggsHasResult(AggregationsContainer<?> aggregationsContainer, String aggsName) {
Aggregations aggregations = ((ElasticsearchAggregations) aggregationsContainer).aggregations();
assertThat(aggregations.asMap().get(aggsName)).isNotNull();
}
protected Query getPipelineAggsQuery(String aggsName, String aggsField, String aggsNamePipeline, String bucketsPath) {
return new NativeSearchQueryBuilder() //
.withQuery(matchAllQuery()) //
.withSearchType(SearchType.DEFAULT) //
.withAggregations(terms(aggsName).field(aggsField)) //
.withPipelineAggregations(statsBucket(aggsNamePipeline, bucketsPath)) //
.withMaxResults(0) //
.build();
}
protected void assertThatPipelineAggsAreCorrect(AggregationsContainer<?> aggregationsContainer, String aggsName,
String pipelineAggsName) {
Aggregations aggregations = ((ElasticsearchAggregations) aggregationsContainer).aggregations();
assertThat(aggregations.asMap().get(aggsName)).isNotNull();
Aggregation keyword_bucket_stats = aggregations.asMap().get(pipelineAggsName);
assertThat(keyword_bucket_stats).isInstanceOf(StatsBucket.class);
if (keyword_bucket_stats instanceof ParsedStatsBucket statsBucket) {
// Rest client
assertThat(statsBucket.getMin()).isEqualTo(1.0);
assertThat(statsBucket.getMax()).isEqualTo(3.0);
assertThat(statsBucket.getAvg()).isEqualTo(2.0);
assertThat(statsBucket.getSum()).isEqualTo(6.0);
assertThat(statsBucket.getCount()).isEqualTo(3L);
}
}
}

View File

@ -1,25 +0,0 @@
/*
* Copyright 2021-2023 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.cluster;
import org.springframework.data.elasticsearch.junit.jupiter.ElasticsearchRestTemplateConfiguration;
import org.springframework.test.context.ContextConfiguration;
/**
* @author Peter-Josef Meisch
*/
@ContextConfiguration(classes = { ElasticsearchRestTemplateConfiguration.class })
public class ClusterOperationsERHLCIntegrationTests extends ClusterOperationsIntegrationTests {}

View File

@ -1,25 +0,0 @@
/*
* Copyright 2021-2023 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.cluster;
import org.springframework.data.elasticsearch.junit.jupiter.ReactiveElasticsearchRestTemplateConfiguration;
import org.springframework.test.context.ContextConfiguration;
/**
* @author Peter-Josef Meisch
*/
@ContextConfiguration(classes = { ReactiveElasticsearchRestTemplateConfiguration.class })
public class ClusterOperationsReactiveERHLCIntegrationTests extends ClusterOperationsReactiveIntegrationTests {}

View File

@ -1,276 +0,0 @@
/*
* Copyright 2019-2023 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.document;
import static org.assertj.core.api.Assertions.*;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.common.bytes.BytesArray;
import org.elasticsearch.common.document.DocumentField;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.get.GetResult;
import org.elasticsearch.index.shard.ShardId;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchShardTarget;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.data.elasticsearch.client.erhlc.DocumentAdapters;
/**
* Unit tests for {@link DocumentAdapters}.
*
* @author Mark Paluch
* @author Peter-Josef Meisch
* @author Roman Puchkovskiy
* @author Matt Gilene
*/
public class DocumentAdaptersUnitTests {
@Test // DATAES-628, DATAES-848
public void shouldAdaptGetResponse() {
Map<String, DocumentField> fields = Collections.singletonMap("field",
new DocumentField("field", Collections.singletonList("value")));
GetResult getResult = new GetResult("index", "type", "my-id", 1, 2, 42, true, null, fields, null);
GetResponse response = new GetResponse(getResult);
Document document = DocumentAdapters.from(response);
assertThat(document.getIndex()).isEqualTo("index");
assertThat(document.hasId()).isTrue();
assertThat(document.getId()).isEqualTo("my-id");
assertThat(document.hasVersion()).isTrue();
assertThat(document.getVersion()).isEqualTo(42);
assertThat(document.get("field")).isEqualTo("value");
assertThat(document.hasSeqNo()).isTrue();
assertThat(document.getSeqNo()).isEqualTo(1);
assertThat(document.hasPrimaryTerm()).isTrue();
assertThat(document.getPrimaryTerm()).isEqualTo(2);
}
@Test // DATAES-628, DATAES-848
public void shouldAdaptGetResponseSource() {
BytesArray source = new BytesArray("{\"field\":\"value\"}");
GetResult getResult = new GetResult("index", "type", "my-id", 1, 2, 42, true, source, Collections.emptyMap(), null);
GetResponse response = new GetResponse(getResult);
Document document = DocumentAdapters.from(response);
assertThat(document.getIndex()).isEqualTo("index");
assertThat(document.hasId()).isTrue();
assertThat(document.getId()).isEqualTo("my-id");
assertThat(document.hasVersion()).isTrue();
assertThat(document.getVersion()).isEqualTo(42);
assertThat(document.get("field")).isEqualTo("value");
assertThat(document.hasSeqNo()).isTrue();
assertThat(document.getSeqNo()).isEqualTo(1);
assertThat(document.hasPrimaryTerm()).isTrue();
assertThat(document.getPrimaryTerm()).isEqualTo(2);
}
@Test // DATAES-799, DATAES-848
public void shouldAdaptGetResult() {
Map<String, DocumentField> fields = Collections.singletonMap("field",
new DocumentField("field", Collections.singletonList("value")));
GetResult getResult = new GetResult("index", "type", "my-id", 1, 2, 42, true, null, fields, null);
Document document = DocumentAdapters.from(getResult);
assertThat(document.getIndex()).isEqualTo("index");
assertThat(document.hasId()).isTrue();
assertThat(document.getId()).isEqualTo("my-id");
assertThat(document.hasVersion()).isTrue();
assertThat(document.getVersion()).isEqualTo(42);
assertThat(document.get("field")).isEqualTo("value");
assertThat(document.hasSeqNo()).isTrue();
assertThat(document.getSeqNo()).isEqualTo(1);
assertThat(document.hasPrimaryTerm()).isTrue();
assertThat(document.getPrimaryTerm()).isEqualTo(2);
}
@Test // DATAES-799, DATAES-848
public void shouldAdaptGetResultSource() {
BytesArray source = new BytesArray("{\"field\":\"value\"}");
GetResult getResult = new GetResult("index", "type", "my-id", 1, 2, 42, true, source, Collections.emptyMap(), null);
Document document = DocumentAdapters.from(getResult);
assertThat(document.getIndex()).isEqualTo("index");
assertThat(document.hasId()).isTrue();
assertThat(document.getId()).isEqualTo("my-id");
assertThat(document.hasVersion()).isTrue();
assertThat(document.getVersion()).isEqualTo(42);
assertThat(document.get("field")).isEqualTo("value");
assertThat(document.hasSeqNo()).isTrue();
assertThat(document.getSeqNo()).isEqualTo(1);
assertThat(document.hasPrimaryTerm()).isTrue();
assertThat(document.getPrimaryTerm()).isEqualTo(2);
}
@Test // DATAES-628, DATAES-848
public void shouldAdaptSearchResponse() {
Map<String, DocumentField> fields = Collections.singletonMap("field",
new DocumentField("field", Collections.singletonList("value")));
SearchShardTarget shard = new SearchShardTarget("node", new ShardId("index", "uuid", 42), null);
SearchHit searchHit = new SearchHit(123, "my-id", new Text("type"), null, fields);
searchHit.shard(shard);
searchHit.setSeqNo(1);
searchHit.setPrimaryTerm(2);
searchHit.score(42);
SearchDocument document = DocumentAdapters.from(searchHit);
assertThat(document.getIndex()).isEqualTo("index");
assertThat(document.hasId()).isTrue();
assertThat(document.getId()).isEqualTo("my-id");
assertThat(document.hasVersion()).isFalse();
assertThat(document.getScore()).isBetween(42f, 42f);
assertThat(document.get("field")).isEqualTo("value");
assertThat(document.hasSeqNo()).isTrue();
assertThat(document.getSeqNo()).isEqualTo(1);
assertThat(document.hasPrimaryTerm()).isTrue();
assertThat(document.getPrimaryTerm()).isEqualTo(2);
}
@Test // DATAES-628
public void searchResponseShouldReturnContainsKey() {
Map<String, DocumentField> fields = new LinkedHashMap<>();
fields.put("string", new DocumentField("string", Collections.singletonList("value")));
fields.put("bool", new DocumentField("bool", Arrays.asList(true, true, false)));
SearchHit searchHit = new SearchHit(123, "my-id", new Text("type"), fields, null);
SearchDocument document = DocumentAdapters.from(searchHit);
assertThat(document.containsKey("string")).isTrue();
assertThat(document.containsKey("not-set")).isFalse();
}
@Test // DATAES-628
public void searchResponseShouldReturnContainsValue() {
Map<String, DocumentField> fields = new LinkedHashMap<>();
fields.put("string", new DocumentField("string", Collections.singletonList("value")));
fields.put("bool", new DocumentField("bool", Arrays.asList(true, true, false)));
fields.put("null", new DocumentField("null", Collections.emptyList()));
SearchHit searchHit = new SearchHit(123, "my-id", new Text("type"), fields, null);
SearchDocument document = DocumentAdapters.from(searchHit);
assertThat(document.containsValue("value")).isTrue();
assertThat(document.containsValue(Arrays.asList(true, true, false))).isTrue();
assertThat(document.containsValue(null)).isTrue();
}
@Test // DATAES-628
public void shouldRenderToJson() {
Map<String, DocumentField> fields = new LinkedHashMap<>();
fields.put("string", new DocumentField("string", Collections.singletonList("value")));
fields.put("bool", new DocumentField("bool", Arrays.asList(true, true, false)));
SearchHit searchHit = new SearchHit(123, "my-id", new Text("type"), fields, null);
SearchDocument document = DocumentAdapters.from(searchHit);
assertThat(document.toJson()).isEqualTo("{\"string\":\"value\",\"bool\":[true,true,false]}");
}
@Test // DATAES-628, DATAES-848
public void shouldAdaptSearchResponseSource() {
BytesArray source = new BytesArray("{\"field\":\"value\"}");
SearchShardTarget shard = new SearchShardTarget("node", new ShardId("index", "uuid", 42), null);
SearchHit searchHit = new SearchHit(123, "my-id", new Text("type"), null, null);
searchHit.shard(shard);
searchHit.sourceRef(source).score(42);
searchHit.version(22);
searchHit.setSeqNo(1);
searchHit.setPrimaryTerm(2);
SearchDocument document = DocumentAdapters.from(searchHit);
assertThat(document.getIndex()).isEqualTo("index");
assertThat(document.hasId()).isTrue();
assertThat(document.getId()).isEqualTo("my-id");
assertThat(document.hasVersion()).isTrue();
assertThat(document.getVersion()).isEqualTo(22);
assertThat(document.getScore()).isBetween(42f, 42f);
assertThat(document.get("field")).isEqualTo("value");
assertThat(document.hasSeqNo()).isTrue();
assertThat(document.getSeqNo()).isEqualTo(1);
assertThat(document.hasPrimaryTerm()).isTrue();
assertThat(document.getPrimaryTerm()).isEqualTo(2);
}
@Test // #725
@DisplayName("should adapt returned explanations")
void shouldAdaptReturnedExplanations() {
SearchHit searchHit = new SearchHit(42);
searchHit.explanation(org.apache.lucene.search.Explanation.match( //
3.14, //
"explanation 3.14", //
Collections.singletonList(org.apache.lucene.search.Explanation.noMatch( //
"explanation noMatch", //
Collections.emptyList()))));
SearchDocument searchDocument = DocumentAdapters.from(searchHit);
Explanation explanation = searchDocument.getExplanation();
assertThat(explanation).isNotNull();
assertThat(explanation.isMatch()).isTrue();
assertThat(explanation.getValue()).isEqualTo(3.14);
assertThat(explanation.getDescription()).isEqualTo("explanation 3.14");
List<Explanation> details = explanation.getDetails();
assertThat(details).containsExactly(new Explanation(false, 0.0, "explanation noMatch", Collections.emptyList()));
}
@Test // DATAES-979
@DisplayName("should adapt returned matched queries")
void shouldAdaptReturnedMatchedQueries() {
SearchHit searchHit = new SearchHit(42);
searchHit.matchedQueries(new String[] { "query1", "query2" });
SearchDocument searchDocument = DocumentAdapters.from(searchHit);
List<String> matchedQueries = searchDocument.getMatchedQueries();
assertThat(matchedQueries).isNotNull();
assertThat(matchedQueries).hasSize(2);
assertThat(matchedQueries).isEqualTo(Arrays.asList("query1", "query2"));
}
}

View File

@ -1,41 +0,0 @@
/*
* Copyright 2020-2023 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.event;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.elasticsearch.junit.jupiter.ElasticsearchRestTemplateConfiguration;
import org.springframework.data.elasticsearch.utils.IndexNameProvider;
import org.springframework.test.context.ContextConfiguration;
/**
* @author Peter-Josef Meisch
*/
@ContextConfiguration(classes = { CallbackERHLCIntegrationTests.Config.class })
class CallbackERHLCIntegrationTests extends CallbackIntegrationTests {
@Configuration
@Import({ ElasticsearchRestTemplateConfiguration.class, CallbackIntegrationTests.Config.class })
static class Config {
@Bean
IndexNameProvider indexNameProvider() {
return new IndexNameProvider("callback-es7");
}
}
}

View File

@ -1,40 +0,0 @@
/*
* Copyright 2021-2023 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.event;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.elasticsearch.junit.jupiter.ReactiveElasticsearchRestTemplateConfiguration;
import org.springframework.data.elasticsearch.utils.IndexNameProvider;
import org.springframework.test.context.ContextConfiguration;
/**
* @author Peter-Josef Meisch
* @since 4.4
*/
@ContextConfiguration(classes = { ReactiveCallbackERHLCIntegrationTests.Config.class })
public class ReactiveCallbackERHLCIntegrationTests extends ReactiveCallbackIntegrationTests {
@Configuration
@Import({ ReactiveElasticsearchRestTemplateConfiguration.class, ReactiveCallbackIntegrationTests.Config.class })
static class Config {
@Bean
IndexNameProvider indexNameProvider() {
return new IndexNameProvider("reactive-callback-es7");
}
}
}

View File

@ -1,56 +0,0 @@
/*
* Copyright 2019-2023 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.geo;
import org.elasticsearch.index.query.QueryBuilders;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.elasticsearch.client.erhlc.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.data.elasticsearch.junit.jupiter.ElasticsearchRestTemplateConfiguration;
import org.springframework.data.elasticsearch.repository.config.EnableElasticsearchRepositories;
import org.springframework.data.elasticsearch.utils.IndexNameProvider;
import org.springframework.test.context.ContextConfiguration;
/**
* @author Peter-Josef Meisch
*/
@ContextConfiguration(classes = { GeoERHLCIntegrationTests.Config.class })
public class GeoERHLCIntegrationTests extends GeoIntegrationTests {
@Configuration
@Import({ ElasticsearchRestTemplateConfiguration.class })
@EnableElasticsearchRepositories(considerNestedRepositories = true)
static class Config {
@Bean
IndexNameProvider indexNameProvider() {
return new IndexNameProvider("geo-integration-es7");
}
}
@Override
protected Query nativeQueryForBoundingBox(String fieldName, double top, double left, double bottom, double right) {
return new NativeSearchQueryBuilder()
.withFilter(QueryBuilders.geoBoundingBoxQuery(fieldName).setCorners(top, left, bottom, right)).build();
}
@Override
protected Query nativeQueryForBoundingBox(String fieldName, String geoHash) {
return new NativeSearchQueryBuilder().withFilter(QueryBuilders.geoBoundingBoxQuery(fieldName).setCorners(geoHash))
.build();
}
}

View File

@ -1,41 +0,0 @@
/*
* Copyright 2020-2023 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.geo;
import org.junit.jupiter.api.DisplayName;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.elasticsearch.junit.jupiter.ElasticsearchRestTemplateConfiguration;
import org.springframework.data.elasticsearch.utils.IndexNameProvider;
import org.springframework.test.context.ContextConfiguration;
/**
* @author Peter-Josef Meisch
*/
@ContextConfiguration(classes = { GeoJsonERHLCIntegrationTests.Config.class })
@DisplayName("GeoJson integration test with RestHighLevelClient")
public class GeoJsonERHLCIntegrationTests extends GeoJsonIntegrationTests {
@Configuration
@Import({ ElasticsearchRestTemplateConfiguration.class })
static class Config {
@Bean
IndexNameProvider indexNameProvider() {
return new IndexNameProvider("geojson-integration-es7");
}
}
}

View File

@ -1,39 +0,0 @@
/*
* Copyright 2021-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.elasticsearch.core.index;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.elasticsearch.junit.jupiter.ElasticsearchRestTemplateConfiguration;
import org.springframework.data.elasticsearch.utils.IndexNameProvider;
import org.springframework.test.context.ContextConfiguration;
/**
* @author Peter-Josef Meisch
*/
@ContextConfiguration(classes = { IndexOperationsERHLCIntegrationTests.Config.class })
public class IndexOperationsERHLCIntegrationTests extends IndexOperationsIntegrationTests {
@Configuration
@Import({ ElasticsearchRestTemplateConfiguration.class })
static class Config {
@Bean
IndexNameProvider indexNameProvider() {
return new IndexNameProvider("indexoperations-es7");
}
}
}

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