Add new Elasticsearch client as an alternative to the existing REST client.

Original Pull Request #2160
Closes #1973

(cherry picked from commit c0b26a51f1299fdc20998cc54c82ffbc7c005dae)
This commit is contained in:
Peter-Josef Meisch 2022-05-12 07:32:39 +02:00
parent 0950dd6c7a
commit a86658c397
No known key found for this signature in database
GPG Key ID: DE108246970C7708
26 changed files with 329 additions and 138 deletions

View File

@ -31,6 +31,7 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import org.springframework.data.elasticsearch.core.convert.GeoConverters;
@ -67,9 +68,13 @@ class CriteriaFilterProcessor {
for (Criteria chainedCriteria : criteria.getCriteriaChain()) {
if (chainedCriteria.isOr()) {
// todo #1973
BoolQuery.Builder boolQueryBuilder = QueryBuilders.bool();
queriesForEntries(chainedCriteria).forEach(boolQueryBuilder::should);
filterQueries.add(new Query(boolQueryBuilder.build()));
} else if (chainedCriteria.isNegating()) {
// todo #1973
Collection<? extends Query> negatingFilters = buildNegatingFilter(criteria.getField().getName(),
criteria.getFilterCriteriaEntries());
filterQueries.addAll(negatingFilters);
} else {
filterQueries.addAll(queriesForEntries(chainedCriteria));
}
@ -85,11 +90,28 @@ class CriteriaFilterProcessor {
BoolQuery.Builder boolQueryBuilder = QueryBuilders.bool();
filterQueries.forEach(boolQueryBuilder::must);
BoolQuery boolQuery = boolQueryBuilder.build();
return Optional.of(boolQuery._toQuery());
return Optional.of(new Query(boolQuery));
}
}
}
private static Collection<? extends Query> buildNegatingFilter(String fieldName,
Set<Criteria.CriteriaEntry> filterCriteriaEntries) {
List<Query> negationFilters = new ArrayList<>();
filterCriteriaEntries.forEach(criteriaEntry -> {
Optional<Query> query = queryFor(criteriaEntry.getKey(), criteriaEntry.getValue(), fieldName);
if (query.isPresent()) {
BoolQuery negatingFilter = QueryBuilders.bool().mustNot(query.get()).build();
negationFilters.add(new Query(negatingFilter));
}
});
return negationFilters;
}
private static Collection<? extends Query> queriesForEntries(Criteria criteria) {
Assert.notNull(criteria.getField(), "criteria must have a field");

View File

@ -80,8 +80,7 @@ final class DocumentAdapters {
Explanation explanation = from(hit.explanation());
// todo #1973 matchedQueries
List<String> matchedQueries = null;
List<String> matchedQueries = hit.matchedQueries();
Function<Map<String, JsonData>, EntityAsMap> fromFields = fields -> {
StringBuilder sb = new StringBuilder("{");

View File

@ -106,7 +106,6 @@ public class ElasticsearchExceptionTranslator implements PersistenceExceptionTra
}
private boolean isSeqNoConflict(Throwable exception) {
// todo #1973 check if this works
Integer status = null;
String message = null;

View File

@ -34,6 +34,8 @@ import java.util.List;
import java.util.Map;
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;
@ -68,6 +70,8 @@ import org.springframework.util.Assert;
*/
public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
private static final Log LOGGER = LogFactory.getLog(ElasticsearchTemplate.class);
private final ElasticsearchClient client;
private final RequestConverter requestConverter;
private final ResponseConverter responseConverter;
@ -249,7 +253,6 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
client -> client.reindex(reindexRequestES));
if (reindexResponse.task() == null) {
// todo #1973 check behaviour and create issue in ES if necessary
throw new UnsupportedBackendOperation("ElasticsearchClient did not return a task id on submit request");
}
@ -447,9 +450,6 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
MultiSearchQueryParameter queryParameter = queryIterator.next();
MultiSearchResponseItem<EntityAsMap> responseItem = responseIterator.next();
// if responseItem kind is Result then responseItem.value is a MultiSearchItem which is derived from
// SearchResponse
if (responseItem.isResult()) {
Class clazz = queryParameter.clazz;
@ -463,7 +463,10 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
searchHitsList.add(searchHits);
} else {
// todo #1973 add failure
if (LOGGER.isWarnEnabled()) {
LOGGER
.warn(String.format("multisearch responsecontains failure: {}", responseItem.failure().error().reason()));
}
}
}

View File

@ -87,10 +87,6 @@ class HighlightQueryBuilder {
builder.boundaryScannerLocale(parameters.getBoundaryScannerLocale());
}
if (parameters.getForceSource()) { // default is false
// todo #1973 parameter missing in new client
}
if (StringUtils.hasLength(parameters.getFragmenter())) {
builder.fragmenter(highlighterFragmenter(parameters.getFragmenter()));
}
@ -111,10 +107,6 @@ class HighlightQueryBuilder {
builder.order(highlighterOrder(parameters.getOrder()));
}
if (parameters.getPhraseLimit() > -1) {
// todo #1973 parameter missing in new client
}
if (parameters.getPreTags().length > 0) {
builder.preTags(Arrays.asList(parameters.getPreTags()));
}

View File

@ -17,6 +17,7 @@ package org.springframework.data.elasticsearch.client.elc;
import co.elastic.clients.elasticsearch._types.FieldValue;
import co.elastic.clients.elasticsearch._types.LatLonGeoLocation;
import co.elastic.clients.elasticsearch._types.query_dsl.IdsQuery;
import co.elastic.clients.elasticsearch._types.query_dsl.MatchAllQuery;
import co.elastic.clients.elasticsearch._types.query_dsl.MatchQuery;
import co.elastic.clients.elasticsearch._types.query_dsl.Operator;
@ -29,6 +30,7 @@ import co.elastic.clients.util.ObjectBuilder;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.List;
import java.util.function.Function;
import org.springframework.data.elasticsearch.core.geo.GeoPoint;
@ -45,6 +47,21 @@ public final class QueryBuilders {
private QueryBuilders() {}
public static IdsQuery idsQuery(List<String> ids) {
Assert.notNull(ids, "ids must not be null");
return IdsQuery.of(i -> i.values(ids));
}
public static Query idsQueryAsQuery(List<String> ids) {
Assert.notNull(ids, "ids must not be null");
Function<Query.Builder, ObjectBuilder<Query>> builder = b -> b.ids(idsQuery(ids));
return builder.apply(new Query.Builder()).build();
}
public static MatchQuery matchQuery(String fieldName, String query, @Nullable Operator operator,
@Nullable Float boost) {

View File

@ -177,8 +177,7 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
return Mono.from(execute( //
(ClientCallback<Publisher<co.elastic.clients.elasticsearch.core.ReindexResponse>>) client -> client
.reindex(reindexRequestES)))
.flatMap(response -> (response.task() == null) ? Mono.error( // todo #1973 check behaviour and create issue in
// ES if necessary
.flatMap(response -> (response.task() == null) ? Mono.error(
new UnsupportedBackendOperation("ElasticsearchClient did not return a task id on submit request"))
: Mono.just(response.task()));
}
@ -499,7 +498,7 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
@Override
public Query idsQuery(List<String> ids) {
throw new UnsupportedOperationException("not implemented");
return NativeQuery.builder().withQuery(QueryBuilders.idsQueryAsQuery(ids)).build();
}
/**

View File

@ -58,6 +58,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
@ -192,7 +193,7 @@ class RequestConverter {
if (filterQuery != null) {
elasticsearchConverter.updateQuery(filterQuery, parameters.getFilterQueryClass());
co.elastic.clients.elasticsearch._types.query_dsl.Query esQuery = getFilter(filterQuery);
co.elastic.clients.elasticsearch._types.query_dsl.Query esQuery = getQuery(filterQuery, null);
if (esQuery != null) {
addActionBuilder.filter(esQuery);
@ -239,7 +240,8 @@ class RequestConverter {
PutMappingRequest.Builder builder = new PutMappingRequest.Builder();
builder.index(Arrays.asList(indexCoordinates.getIndexNames()));
addPropertiesToMapping(builder, mapping);
// TODO #1973 what else to add
// TODO #2155 what else to add
return builder.build();
}
@ -374,7 +376,7 @@ class RequestConverter {
Query filterQuery = parameters.getFilterQuery();
if (filterQuery != null) {
co.elastic.clients.elasticsearch._types.query_dsl.Query esQuery = getFilter(filterQuery);
co.elastic.clients.elasticsearch._types.query_dsl.Query esQuery = getQuery(filterQuery, null);
if (esQuery != null) {
aliasBuilder.filter(esQuery);
@ -475,7 +477,7 @@ class RequestConverter {
}
}
builder.refresh(refresh(refreshPolicy));
builder.refresh(TypeUtils.refresh(refreshPolicy));
return builder.build();
}
@ -644,9 +646,9 @@ class RequestConverter {
builder.timeout(tb -> tb.time(Long.valueOf(bulkOptions.getTimeout().toMillis()).toString() + "ms"));
}
builder.refresh(refresh(refreshPolicy));
builder.refresh(TypeUtils.refresh(refreshPolicy));
if (bulkOptions.getRefreshPolicy() != null) {
builder.refresh(refresh(bulkOptions.getRefreshPolicy()));
builder.refresh(TypeUtils.refresh(bulkOptions.getRefreshPolicy()));
}
if (bulkOptions.getWaitForActiveShards() != null) {
@ -791,13 +793,13 @@ class RequestConverter {
ReindexRequest.Dest dest = reindexRequest.getDest();
return d //
.index(dest.getIndex().getIndexName()) //
.versionType(versionType(dest.getVersionType())) //
.opType(opType(dest.getOpType()));
.versionType(TypeUtils.versionType(dest.getVersionType())) //
.opType(TypeUtils.opType(dest.getOpType()));
} //
);
if (reindexRequest.getConflicts() != null) {
builder.conflicts(conflicts(reindexRequest.getConflicts()));
builder.conflicts(TypeUtils.conflicts(reindexRequest.getConflicts()));
}
ReindexRequest.Script script = reindexRequest.getScript();
@ -810,7 +812,7 @@ class RequestConverter {
if (reindexRequest.getWaitForActiveShards() != null) {
builder.waitForActiveShards(wfas -> wfas //
.count(waitForActiveShardsCount(reindexRequest.getWaitForActiveShards())));
.count(TypeUtils.waitForActiveShardsCount(reindexRequest.getWaitForActiveShards())));
}
builder //
@ -835,7 +837,7 @@ class RequestConverter {
if (routing != null) {
r.routing(routing);
}
r.refresh(refresh(refreshPolicy));
r.refresh(TypeUtils.refresh(refreshPolicy));
return r;
});
}
@ -908,7 +910,7 @@ class RequestConverter {
.docAsUpsert(query.getDocAsUpsert()) //
.ifSeqNo(query.getIfSeqNo() != null ? Long.valueOf(query.getIfSeqNo()) : null) //
.ifPrimaryTerm(query.getIfPrimaryTerm() != null ? Long.valueOf(query.getIfPrimaryTerm()) : null) //
.refresh(refresh(refreshPolicy)) //
.refresh(TypeUtils.refresh(refreshPolicy)) //
.retryOnConflict(query.getRetryOnConflict()) //
;
@ -993,7 +995,7 @@ class RequestConverter {
}
if (updateQuery.getWaitForActiveShards() != null) {
ub.waitForActiveShards(w -> w.count(waitForActiveShardsCount(updateQuery.getWaitForActiveShards())));
ub.waitForActiveShards(w -> w.count(TypeUtils.waitForActiveShardsCount(updateQuery.getWaitForActiveShards())));
}
return ub;
@ -1048,12 +1050,12 @@ class RequestConverter {
mrb.searches(sb -> sb //
.header(h -> h //
.index(param.index.getIndexName()) //
// todo #1973 add remaining flags for header
// todo #2156 add remaining flags for header
) //
.body(bb -> bb //
.query(getQuery(param.query, param.clazz))//
// #1973 seq_no_primary_term and version not available in client ES issue 161
// todo #1973 add remaining flags for body
// todo #2138 seq_no_primary_term and version not available in client ES issue 161
// todo #2156 add remaining flags for body
) //
);
});
@ -1101,7 +1103,7 @@ class RequestConverter {
}
if (query.getIndicesOptions() != null) {
// todo #1973 indices options
// new Elasticsearch client does not support the old Indices options, need to be adapted
}
if (query.isLimiting()) {
@ -1116,7 +1118,7 @@ class RequestConverter {
builder.preference(query.getPreference());
}
// todo #1973 searchType
builder.searchType(searchType(query.getSearchType()));
if (query.getSort() != null) {
List<SortOptions> sortOptions = getSortOptions(query.getSort(), persistentEntity);
@ -1144,7 +1146,7 @@ class RequestConverter {
builder.routing(query.getRoute());
}
// todo #1973 timeout
builder.timeout(timeStringMs(query.getTimeout()));
if (query.getExplain()) {
builder.explain(true);
@ -1158,7 +1160,7 @@ class RequestConverter {
builder.rescore(getRescore(rescorerQuery));
});
// todo #1973 request cache
builder.requestCache(query.getRequestCache());
if (!query.getRuntimeFields().isEmpty()) {
@ -1183,6 +1185,15 @@ class RequestConverter {
// limit the number of documents in a batch
builder.size(500);
}
if (!isEmpty(query.getIndicesBoost())) {
Map<String, Double> boosts = new LinkedHashMap<>();
query.getIndicesBoost().forEach(indexBoost -> {
boosts.put(indexBoost.getIndexName(), (double) indexBoost.getBoost());
});
// noinspection unchecked
builder.indicesBoost(boosts);
}
}
private Rescore getRescore(RescorerQuery rescorerQuery) {
@ -1190,7 +1201,7 @@ class RequestConverter {
return Rescore.of(r -> r //
.query(rq -> rq //
.query(getQuery(rescorerQuery.getQuery(), null)) //
.scoreMode(scoreMode(rescorerQuery.getScoreMode())) //
.scoreMode(TypeUtils.scoreMode(rescorerQuery.getScoreMode())) //
.queryWeight(rescorerQuery.getQueryWeight() != null ? Double.valueOf(rescorerQuery.getQueryWeight()) : 1.0) //
.rescoreQueryWeight(
rescorerQuery.getRescoreQueryWeight() != null ? Double.valueOf(rescorerQuery.getRescoreQueryWeight())
@ -1242,8 +1253,9 @@ class RequestConverter {
.geoDistance(gd -> gd //
.field(fieldName) //
.location(loc -> loc.latlon(QueryBuilders.latLon(geoDistanceOrder.getGeoPoint())))//
.distanceType(geoDistanceType(geoDistanceOrder.getDistanceType())).mode(sortMode(finalMode)) //
.unit(distanceUnit(geoDistanceOrder.getUnit())) //
.distanceType(TypeUtils.geoDistanceType(geoDistanceOrder.getDistanceType()))
.mode(TypeUtils.sortMode(finalMode)) //
.unit(TypeUtils.distanceUnit(geoDistanceOrder.getUnit())) //
.ignoreUnmapped(geoDistanceOrder.getIgnoreUnmapped())));
} else {
String missing = (order.getNullHandling() == Sort.NullHandling.NULLS_FIRST) ? "_first"
@ -1253,10 +1265,10 @@ class RequestConverter {
.field(f -> {
f.field(fieldName) //
.order(sortOrder) //
.mode(sortMode(finalMode));
.mode(TypeUtils.sortMode(finalMode));
if (finalUnmappedType != null) {
FieldType fieldType = fieldType(finalUnmappedType);
FieldType fieldType = TypeUtils.fieldType(finalUnmappedType);
if (fieldType != null) {
f.unmappedType(fieldType);
@ -1284,13 +1296,11 @@ class RequestConverter {
.collapse(query.getFieldCollapse()) //
;
// todo #1973 indices boost
if (!isEmpty(query.getAggregations())) {
builder.aggregations(query.getAggregations());
}
// todo #1973 searchExt
// todo #2150 searchExt, currently not supported by the new client
}
@Nullable
@ -1463,12 +1473,6 @@ class RequestConverter {
return versionType != null ? versionType : VersionType.External;
}
private co.elastic.clients.elasticsearch._types.query_dsl.Query getFilter(Query filterQuery) {
// TODO #1973 add filter query
throw new UnsupportedOperationException("not implemented");
}
@Nullable
private SourceConfig getSourceConfig(Query query) {

View File

@ -116,7 +116,7 @@ class SearchDocumentResponseBuilder {
ElasticsearchAggregations aggregationsContainer = aggregations != null ? new ElasticsearchAggregations(aggregations)
: null;
// todo #1973
// todo #2154
Suggest suggest = null;
return new SearchDocumentResponse(totalHits, totalHitsRelation, maxScore, scrollId, searchDocuments,

View File

@ -15,15 +15,7 @@
*/
package org.springframework.data.elasticsearch.client.elc;
import co.elastic.clients.elasticsearch._types.Conflicts;
import co.elastic.clients.elasticsearch._types.DistanceUnit;
import co.elastic.clients.elasticsearch._types.GeoDistanceType;
import co.elastic.clients.elasticsearch._types.OpType;
import co.elastic.clients.elasticsearch._types.Refresh;
import co.elastic.clients.elasticsearch._types.Result;
import co.elastic.clients.elasticsearch._types.SortMode;
import co.elastic.clients.elasticsearch._types.Time;
import co.elastic.clients.elasticsearch._types.VersionType;
import co.elastic.clients.elasticsearch._types.*;
import co.elastic.clients.elasticsearch._types.mapping.FieldType;
import co.elastic.clients.elasticsearch.core.search.BoundaryScanner;
import co.elastic.clients.elasticsearch.core.search.BuiltinHighlighterType;
@ -40,6 +32,7 @@ import org.springframework.data.elasticsearch.core.RefreshPolicy;
import org.springframework.data.elasticsearch.core.query.GeoDistanceOrder;
import org.springframework.data.elasticsearch.core.query.IndexQuery;
import org.springframework.data.elasticsearch.core.query.Order;
import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.data.elasticsearch.core.query.RescorerQuery;
import org.springframework.data.elasticsearch.core.query.UpdateResponse;
import org.springframework.data.elasticsearch.core.reindex.ReindexRequest;
@ -298,6 +291,23 @@ final class TypeUtils {
return null;
}
@Nullable
static SearchType searchType(@Nullable Query.SearchType searchType) {
if (searchType == null) {
return null;
}
switch (searchType) {
case QUERY_THEN_FETCH:
return SearchType.QueryThenFetch;
case DFS_QUERY_THEN_FETCH:
return SearchType.DfsQueryThenFetch;
}
return null;
}
@Nullable
static SortMode sortMode(Order.Mode mode) {
@ -325,6 +335,16 @@ final class TypeUtils {
return Time.of(t -> t.time(duration.toMillis() + "ms"));
}
@Nullable
static String timeStringMs(@Nullable Duration duration) {
if (duration == null) {
return null;
}
return duration.toMillis() + "ms";
}
@Nullable
static VersionType versionType(
@Nullable org.springframework.data.elasticsearch.annotations.Document.VersionType versionType) {

View File

@ -277,8 +277,7 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
*/
protected Mono<GetResult> doGet(GetRequest request) {
return Mono.from(execute(client -> client.get(request))) //
.onErrorResume(NoSuchIndexException.class, it -> Mono.empty());
return Mono.from(execute(client -> client.get(request)));
}
protected Mono<String> doDeleteById(String id, @Nullable String routing, IndexCoordinates index) {
@ -633,8 +632,7 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
QUERY_LOGGER.debug(String.format("Executing doCount: %s", request));
}
return Mono.from(execute(client -> client.count(request))) //
.onErrorResume(NoSuchIndexException.class, it -> Mono.just(0L));
return Mono.from(execute(client -> client.count(request)));
}
/**

View File

@ -22,7 +22,8 @@ import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
* class that holds explanations returned from an Elasticsearch search.
* class that holds explanations returned from an Elasticsearch search. Note: the new Elasticsearch client does not
* return the match property in search hits anymore, probably because a returned hit always is a match.
*
* @author Peter-Josef Meisch
*/

View File

@ -71,6 +71,7 @@ public class BaseQuery implements Query {
@Nullable protected Boolean requestCache;
protected List<IdWithRouting> idsWithRouting = Collections.emptyList();
protected final List<RuntimeField> runtimeFields = new ArrayList<>();
@Nullable protected List<IndexBoost> indicesBoost;
public BaseQuery() {}
@ -88,7 +89,7 @@ public class BaseQuery implements Query {
this.fields = builder.getFields();
this.highlightQuery = builder.highlightQuery;
this.route = builder.getRoute();
// #1973 add the other fields to the builder
this.indicesBoost = builder.getIndicesBoost();
}
@Override
@ -433,4 +434,10 @@ public class BaseQuery implements Query {
public List<RuntimeField> getRuntimeFields() {
return runtimeFields;
}
@Override
@Nullable
public List<IndexBoost> getIndicesBoost() {
return indicesBoost;
}
}

View File

@ -46,6 +46,7 @@ public abstract class BaseQueryBuilder<Q extends BaseQuery, SELF extends BaseQue
private List<String> fields = new ArrayList<>();
@Nullable protected HighlightQuery highlightQuery;
@Nullable private String route;
@Nullable private List<IndexBoost> indicesBoost;
@Nullable
public Pageable getPageable() {
@ -104,6 +105,11 @@ public abstract class BaseQueryBuilder<Q extends BaseQuery, SELF extends BaseQue
return route;
}
@Nullable
public List<IndexBoost> getIndicesBoost() {
return indicesBoost;
}
public SELF withPageable(Pageable pageable) {
this.pageable = pageable;
return self();
@ -178,6 +184,16 @@ public abstract class BaseQueryBuilder<Q extends BaseQuery, SELF extends BaseQue
return self();
}
public SELF withIndicesBoost(List<IndexBoost> indicesBoost) {
this.indicesBoost = indicesBoost;
return self();
}
public SELF withIndicesBoost(IndexBoost... indicesBoost) {
this.indicesBoost = Arrays.asList(indicesBoost);
return self();
}
public abstract Q build();
private SELF self() {

View File

@ -55,7 +55,6 @@ public class NativeSearchQuery extends BaseQuery {
@Nullable private List<PipelineAggregationBuilder> pipelineAggregations;
@Nullable private HighlightBuilder highlightBuilder;
@Nullable private HighlightBuilder.Field[] highlightFields;
@Nullable private List<IndexBoost> indicesBoost;
@Nullable private SearchTemplateRequestBuilder searchTemplate;
@Nullable private SuggestBuilder suggestBuilder;
@Nullable private List<SearchExtBuilder> searchExtBuilders;
@ -182,11 +181,6 @@ public class NativeSearchQuery extends BaseQuery {
this.pipelineAggregations = pipelineAggregationBuilders;
}
@Nullable
public List<IndexBoost> getIndicesBoost() {
return indicesBoost;
}
public void setIndicesBoost(List<IndexBoost> indicesBoost) {
this.indicesBoost = indicesBoost;
}

View File

@ -63,7 +63,6 @@ public class NativeSearchQueryBuilder extends BaseQueryBuilder<NativeSearchQuery
@Nullable private List<HighlightBuilder.Field> highlightFields = new ArrayList<>();
@Nullable protected List<String> storedFields;
@Nullable private CollapseBuilder collapseBuilder;
@Nullable private List<IndexBoost> indicesBoost = new ArrayList<>();
@Nullable private SearchTemplateRequestBuilder searchTemplateBuilder;
@Nullable private SearchType searchType;
@Nullable private Boolean trackTotalHits;
@ -194,19 +193,6 @@ public class NativeSearchQueryBuilder extends BaseQueryBuilder<NativeSearchQuery
return this;
}
public NativeSearchQueryBuilder withIndicesBoost(Collection<IndexBoost> indicesBoost) {
this.indicesBoost.addAll(indicesBoost);
return this;
}
/**
* @since 4.3
*/
public NativeSearchQueryBuilder withIndicesBoost(IndexBoost... indicesBoost) {
Collections.addAll(this.indicesBoost, indicesBoost);
return this;
}
public NativeSearchQueryBuilder withSearchTemplate(SearchTemplateRequestBuilder searchTemplateBuilder) {
this.searchTemplateBuilder = searchTemplateBuilder;
return this;
@ -290,10 +276,6 @@ public class NativeSearchQueryBuilder extends BaseQueryBuilder<NativeSearchQuery
nativeSearchQuery.setStoredFields(storedFields);
}
if (indicesBoost != null) {
nativeSearchQuery.setIndicesBoost(indicesBoost);
}
if (searchTemplateBuilder != null) {
nativeSearchQuery.setSearchTemplate(searchTemplateBuilder);
}

View File

@ -433,6 +433,12 @@ public interface Query {
*/
List<RuntimeField> getRuntimeFields();
/**
* @since 4.4
*/
@Nullable
List<IndexBoost> getIndicesBoost();
/**
* @since 4.3
*/

View File

@ -1,2 +1,2 @@
version.spring-data-elasticsearch=${project.version}
version.elasticsearch-client=${elasticsearch}
version.elasticsearch-client=${elasticsearch-rhlc}

View File

@ -16,7 +16,7 @@
package org.springframework.data.elasticsearch;
/**
* TODO #1973 remove when the new Elasticsearch client is fully working
* TODO remove when the new Elasticsearch client is fully working
*
* @author Peter-Josef Meisch
*/

View File

@ -20,12 +20,15 @@ import co.elastic.clients.json.JsonData;
import co.elastic.clients.json.JsonpMapper;
import co.elastic.clients.json.jackson.JacksonJsonpMapper;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.assertj.core.api.SoftAssertions;
import org.assertj.core.data.Offset;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.data.elasticsearch.core.document.Explanation;
import org.springframework.data.elasticsearch.core.document.SearchDocument;
/**
@ -106,4 +109,53 @@ class DocumentAdaptersUnitTests {
softly.assertAll();
}
@Test // #725 #1973
@DisplayName("should adapt returned explanations")
void shouldAdaptReturnedExplanations() {
Hit<EntityAsMap> searchHit = new Hit.Builder<EntityAsMap>() //
.index("index") //
.id("42") //
.explanation(eb -> eb //
.value(3.14f) //
.description("explanation 3.14") //
.details(edb -> edb.description("explanation noMatch").value(0f)))
.build();
SearchDocument searchDocument = DocumentAdapters.from(searchHit, jsonpMapper);
SoftAssertions softly = new SoftAssertions();
Explanation explanation = searchDocument.getExplanation();
softly.assertThat(explanation).isNotNull();
softly.assertThat(explanation.isMatch()).isTrue();
softly.assertThat(explanation.getValue()).isCloseTo(3.14, Offset.offset(0.001));
softly.assertThat(explanation.getDescription()).isEqualTo("explanation 3.14");
List<Explanation> details = explanation.getDetails();
softly.assertThat(details)
.containsExactly(new Explanation(null, 0.0, "explanation noMatch", Collections.emptyList()));
softly.assertAll();
}
@Test // DATAES-979 #1973
@DisplayName("should adapt returned matched queries")
void shouldAdaptReturnedMatchedQueries() {
Hit<EntityAsMap> searchHit = new Hit.Builder<EntityAsMap>() //
.index("index") //
.id("42") //
.matchedQueries("query1", "query2") //
.build();
SearchDocument searchDocument = DocumentAdapters.from(searchHit, jsonpMapper);
SoftAssertions softly = new SoftAssertions();
List<String> matchedQueries = searchDocument.getMatchedQueries();
softly.assertThat(matchedQueries).isNotNull();
softly.assertThat(matchedQueries).hasSize(2);
softly.assertThat(matchedQueries).isEqualTo(Arrays.asList("query1", "query2"));
softly.assertAll();
}
}

View File

@ -1617,7 +1617,7 @@ public abstract class ElasticsearchIntegrationTests implements NewElasticsearchC
}
@DisabledIf(value = "newElasticsearchClient",
disabledReason = "todo #1973 can't check response, open ES issue 161 that does not allow seqno")
disabledReason = "todo #2138 can't check response, open ES issue 161 that does not allow seqno")
// and version to be set in the request
@Test // DATAES-487
public void shouldReturnSameEntityForMultiSearch() {
@ -1642,7 +1642,7 @@ public abstract class ElasticsearchIntegrationTests implements NewElasticsearchC
}
@DisabledIf(value = "newElasticsearchClient",
disabledReason = "todo #1973 can't check response, open ES issue 161 that does not allow seqno")
disabledReason = "todo #2138 can't check response, open ES issue 161 that does not allow seqno")
// and version to be set in the request
@Test // DATAES-487
public void shouldReturnDifferentEntityForMultiSearch() {
@ -3070,7 +3070,7 @@ public abstract class ElasticsearchIntegrationTests implements NewElasticsearchC
}
@DisabledIf(value = "newElasticsearchClient",
disabledReason = "todo #1973 can't check response, open ES issue 161 that does not allow seqno")
disabledReason = "todo #2138 can't check response, open ES issue 161 that does not allow seqno")
// and version to be set in the request
@Test // DATAES-799
void multiSearchShouldReturnSeqNoPrimaryTerm() {

View File

@ -116,7 +116,7 @@ public abstract class CompletionIntegrationTests implements NewElasticsearchClie
operations.bulkIndex(indexQueries, AnnotatedCompletionEntity.class);
}
@DisabledIf(value = "newElasticsearchClient", disabledReason="todo #1973, ES issue 150")
@DisabledIf(value = "newElasticsearchClient", disabledReason = "todo #2139, ES issue 150")
@Test
public void shouldFindSuggestionsForGivenCriteriaQueryUsingCompletionEntity() {
@ -148,7 +148,7 @@ public abstract class CompletionIntegrationTests implements NewElasticsearchClie
operations.get("1", CompletionEntity.class);
}
@DisabledIf(value = "newElasticsearchClient", disabledReason="todo #1973, ES issue 150")
@DisabledIf(value = "newElasticsearchClient", disabledReason = "todo #2139, ES issue 150")
@Test
public void shouldFindSuggestionsForGivenCriteriaQueryUsingAnnotatedCompletionEntity() {
@ -172,7 +172,7 @@ public abstract class CompletionIntegrationTests implements NewElasticsearchClie
assertThat(options.get(1).getText()).isIn("Marchand", "Mohsin");
}
@DisabledIf(value = "newElasticsearchClient", disabledReason="todo #1973, ES issue 150")
@DisabledIf(value = "newElasticsearchClient", disabledReason = "todo #2139, ES 1issue 150")
@Test
public void shouldFindSuggestionsWithWeightsForGivenCriteriaQueryUsingAnnotatedCompletionEntity() {

View File

@ -67,7 +67,7 @@ public abstract class ReactiveSuggestIntegrationTests implements NewElasticsearc
operations.indexOps(IndexCoordinates.of(indexNameProvider.getPrefix() + "*")).delete().block();
}
@DisabledIf(value = "newElasticsearchClient", disabledReason="todo #1973, ES issue 150")
@DisabledIf(value = "newElasticsearchClient", disabledReason = "todo #2139, ES issue 150")
@Test // #1302
@DisplayName("should find suggestions for given prefix completion")
void shouldFindSuggestionsForGivenPrefixCompletion() {

View File

@ -0,0 +1,44 @@
/*
* Copyright 2022 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.repository.support;
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.ReactiveElasticsearchTemplateConfiguration;
import org.springframework.data.elasticsearch.repository.config.EnableReactiveElasticsearchRepositories;
import org.springframework.data.elasticsearch.utils.IndexNameProvider;
import org.springframework.test.context.ContextConfiguration;
/**
* @author Peter-Josef Meisch
* @since 4.4
*/
@ContextConfiguration(classes = { SimpleReactiveElasticsearchRepositoryELCIntegrationTests.Config.class })
public class SimpleReactiveElasticsearchRepositoryELCIntegrationTests
extends SimpleReactiveElasticsearchRepositoryIntegrationTests {
@Configuration
@Import({ ReactiveElasticsearchTemplateConfiguration.class })
@EnableReactiveElasticsearchRepositories(considerNestedRepositories = true)
static class Config {
@Bean
IndexNameProvider indexNameProvider() {
return new IndexNameProvider("simple-reactive-repository");
}
}
}

View File

@ -0,0 +1,43 @@
/*
* Copyright 2022 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.repository.support;
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.repository.config.EnableReactiveElasticsearchRepositories;
import org.springframework.data.elasticsearch.utils.IndexNameProvider;
import org.springframework.test.context.ContextConfiguration;
/**
* @author Peter-Josef Meisch
*/
@ContextConfiguration(classes = { SimpleReactiveElasticsearchRepositoryERHLCIntegrationTests.Config.class })
public class SimpleReactiveElasticsearchRepositoryERHLCIntegrationTests
extends SimpleReactiveElasticsearchRepositoryIntegrationTests {
@Configuration
@Import({ ReactiveElasticsearchRestTemplateConfiguration.class })
@EnableReactiveElasticsearchRepositories(considerNestedRepositories = true)
static class Config {
@Bean
IndexNameProvider indexNameProvider() {
return new IndexNameProvider("simple-reactive-repository-es7");
}
}
}

View File

@ -16,81 +16,68 @@
package org.springframework.data.elasticsearch.repository.support;
import static org.assertj.core.api.Assertions.*;
import static org.springframework.data.elasticsearch.annotations.FieldType.*;
import static org.springframework.data.elasticsearch.core.query.Query.*;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import java.lang.Boolean;
import java.lang.Long;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.reactivestreams.Publisher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.Version;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Order;
import org.springframework.data.elasticsearch.RestStatusException;
import org.springframework.data.elasticsearch.NoSuchIndexException;
import org.springframework.data.elasticsearch.annotations.CountQuery;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;
import org.springframework.data.elasticsearch.annotations.Highlight;
import org.springframework.data.elasticsearch.annotations.HighlightField;
import org.springframework.data.elasticsearch.annotations.Query;
import org.springframework.data.elasticsearch.core.ReactiveElasticsearchOperations;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.junit.jupiter.ReactiveElasticsearchRestTemplateConfiguration;
import org.springframework.data.elasticsearch.junit.jupiter.SpringIntegrationTest;
import org.springframework.data.elasticsearch.repository.config.EnableReactiveElasticsearchRepositories;
import org.springframework.data.elasticsearch.utils.IndexNameProvider;
import org.springframework.data.repository.reactive.ReactiveCrudRepository;
import org.springframework.lang.Nullable;
import org.springframework.test.context.ContextConfiguration;
/**
* @author Christoph Strobl
* @author Peter-Josef Meisch
* @author Jens Schauder
*/
// todo #1973 test for both clients
@SpringIntegrationTest
@ContextConfiguration(classes = { SimpleReactiveElasticsearchRepositoryTests.Config.class })
class SimpleReactiveElasticsearchRepositoryTests {
@Configuration
@Import({ ReactiveElasticsearchRestTemplateConfiguration.class })
@EnableReactiveElasticsearchRepositories(considerNestedRepositories = true)
static class Config {}
static final String INDEX = "test-index-sample-simple-reactive";
abstract class SimpleReactiveElasticsearchRepositoryIntegrationTests {
@Autowired ReactiveElasticsearchOperations operations;
@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
@Autowired ReactiveSampleEntityRepository repository;
@Autowired private IndexNameProvider indexNameProvider;
@BeforeEach
void setUp() {
operations.indexOps(IndexCoordinates.of(INDEX)).delete().block();
void before() {
indexNameProvider.increment();
operations.indexOps(SampleEntity.class).createWithMapping().block();
}
@AfterEach
void after() {
operations.indexOps(IndexCoordinates.of(INDEX)).delete().block();
@Test
@org.junit.jupiter.api.Order(Integer.MAX_VALUE)
public void cleanup() {
operations.indexOps(IndexCoordinates.of(indexNameProvider.getPrefix() + "*")).delete().block();
}
@Test // DATAES-519
@ -104,7 +91,7 @@ class SimpleReactiveElasticsearchRepositoryTests {
}
private Mono<Boolean> documentWithIdExistsInIndex(String id) {
return operations.exists(id, IndexCoordinates.of(INDEX));
return operations.exists(id, IndexCoordinates.of(indexNameProvider.indexName()));
}
@Test // DATAES-519
@ -122,9 +109,12 @@ class SimpleReactiveElasticsearchRepositoryTests {
@Test // DATAES-519, DATAES-767, DATAES-822
void findByIdShouldErrorIfIndexDoesNotExist() {
operations.indexOps(SampleEntity.class).delete().block();
repository.findById("id-two") //
.as(StepVerifier::create) //
.expectError(RestStatusException.class);
.expectError(NoSuchIndexException.class) //
.verify();
}
@Test // DATAES-519
@ -268,9 +258,12 @@ class SimpleReactiveElasticsearchRepositoryTests {
@Test // DATAES-519, DATAES-767, DATAES-822
void countShouldErrorWhenIndexDoesNotExist() {
operations.indexOps(SampleEntity.class).delete().block();
repository.count() //
.as(StepVerifier::create) //
.expectError(RestStatusException.class);
.expectError(NoSuchIndexException.class) //
.verify();
}
@Test // DATAES-519
@ -596,7 +589,7 @@ class SimpleReactiveElasticsearchRepositoryTests {
}
Mono<Void> bulkIndex(SampleEntity... entities) {
return operations.saveAll(Arrays.asList(entities), IndexCoordinates.of(INDEX)).then();
return operations.saveAll(Arrays.asList(entities), IndexCoordinates.of(indexNameProvider.indexName())).then();
}
interface ReactiveSampleEntityRepository extends ReactiveCrudRepository<SampleEntity, String> {
@ -636,14 +629,14 @@ class SimpleReactiveElasticsearchRepositoryTests {
Mono<Long> retrieveCountByText(String message);
}
@Document(indexName = INDEX)
@Document(indexName = "#{@indexNameProvider.indexName()}")
static class SampleEntity {
@Nullable
@Id private String id;
@Nullable
@Field(type = Text, store = true, fielddata = true) private String type;
@Field(type = FieldType.Text, store = true, fielddata = true) private String type;
@Nullable
@Field(type = Text, store = true, fielddata = true) private String message;
@Field(type = FieldType.Text, store = true, fielddata = true) private String message;
@Nullable private int rate;
@Nullable private boolean available;
@Nullable