mirror of
https://github.com/spring-projects/spring-data-elasticsearch.git
synced 2025-06-13 07:32:11 +00:00
Remove the integration of the deprecated RestHighLevelClient.
Original Pull Request #2560 Closes #2558
This commit is contained in:
parent
73e9a6f5c5
commit
f464f77985
50
pom.xml
50
pom.xml
@ -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>
|
||||
|
@ -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}.
|
||||
*
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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)));
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
@ -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 {}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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_");
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -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;
|
||||
}
|
||||
}
|
@ -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
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -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;
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -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
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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() : "";
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
@ -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) {}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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.
|
||||
*
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
@ -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() {
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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(); //
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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+)", "");
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -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();
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@ -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");
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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 {}
|
||||
}
|
@ -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 {
|
||||
|
||||
}
|
||||
}
|
@ -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 {}
|
||||
|
||||
|
@ -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> {}
|
||||
}
|
@ -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
|
||||
*/
|
||||
|
@ -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> {}
|
||||
}
|
@ -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");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
@ -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();
|
||||
|
||||
}
|
||||
}
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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
|
||||
|
@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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 {}
|
@ -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 {}
|
@ -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"));
|
||||
}
|
||||
}
|
@ -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");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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
Loading…
x
Reference in New Issue
Block a user