mirror of
https://github.com/spring-projects/spring-data-elasticsearch.git
synced 2025-06-29 15:22:11 +00:00
Support Delete by query with es parameters.
Original Pull Request #2875 Closes #2865
This commit is contained in:
parent
d2b3ba94f6
commit
496b8d62a4
@ -54,6 +54,7 @@ import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
|
||||
import org.springframework.data.elasticsearch.core.query.BaseQueryBuilder;
|
||||
import org.springframework.data.elasticsearch.core.query.BulkOptions;
|
||||
import org.springframework.data.elasticsearch.core.query.ByQueryResponse;
|
||||
import org.springframework.data.elasticsearch.core.query.DeleteQuery;
|
||||
import org.springframework.data.elasticsearch.core.query.IndexQuery;
|
||||
import org.springframework.data.elasticsearch.core.query.MoreLikeThisQuery;
|
||||
import org.springframework.data.elasticsearch.core.query.Query;
|
||||
@ -177,6 +178,11 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
|
||||
doBulkOperation(queries, bulkOptions, index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByQueryResponse delete(DeleteQuery query, Class<?> clazz) {
|
||||
return delete(query, clazz, getIndexCoordinatesFor(clazz));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByQueryResponse delete(Query query, Class<?> clazz, IndexCoordinates index) {
|
||||
|
||||
@ -190,6 +196,18 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
|
||||
return responseConverter.byQueryResponse(response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByQueryResponse delete(DeleteQuery query, Class<?> clazz, IndexCoordinates index) {
|
||||
Assert.notNull(query, "query must not be null");
|
||||
|
||||
DeleteByQueryRequest request = requestConverter.documentDeleteByQueryRequest(query, routingResolver.getRouting(),
|
||||
clazz, index, getRefreshPolicy());
|
||||
|
||||
DeleteByQueryResponse response = execute(client -> client.deleteByQuery(request));
|
||||
|
||||
return responseConverter.byQueryResponse(response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UpdateResponse update(UpdateQuery updateQuery, IndexCoordinates index) {
|
||||
|
||||
|
@ -25,6 +25,7 @@ import co.elastic.clients.elasticsearch.core.search.ResponseBody;
|
||||
import co.elastic.clients.json.JsonpMapper;
|
||||
import co.elastic.clients.transport.Version;
|
||||
import co.elastic.clients.transport.endpoints.BooleanResponse;
|
||||
import org.springframework.data.elasticsearch.core.query.DeleteQuery;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.util.function.Tuple2;
|
||||
@ -180,6 +181,15 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
|
||||
return Mono.from(execute(client -> client.deleteByQuery(request))).map(responseConverter::byQueryResponse);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<ByQueryResponse> delete(DeleteQuery query, Class<?> entityType, IndexCoordinates index) {
|
||||
Assert.notNull(query, "query must not be null");
|
||||
|
||||
DeleteByQueryRequest request = requestConverter.documentDeleteByQueryRequest(query, routingResolver.getRouting(),
|
||||
entityType, index, getRefreshPolicy());
|
||||
return Mono.from(execute(client -> client.deleteByQuery(request))).map(responseConverter::byQueryResponse);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Mono<T> get(String id, Class<T> entityType, IndexCoordinates index) {
|
||||
|
||||
|
@ -968,6 +968,79 @@ class RequestConverter {
|
||||
});
|
||||
}
|
||||
|
||||
public DeleteByQueryRequest documentDeleteByQueryRequest(DeleteQuery query, @Nullable String routing, Class<?> clazz,
|
||||
IndexCoordinates index, @Nullable RefreshPolicy refreshPolicy) {
|
||||
Assert.notNull(query, "query must not be null");
|
||||
Assert.notNull(index, "index must not be null");
|
||||
|
||||
return DeleteByQueryRequest.of(dqb -> {
|
||||
dqb.index(Arrays.asList(index.getIndexNames())) //
|
||||
.query(getQuery(query.getQuery(), clazz))//
|
||||
.refresh(deleteByQueryRefresh(refreshPolicy))
|
||||
.requestsPerSecond(query.getRequestsPerSecond())
|
||||
.maxDocs(query.getMaxDocs())
|
||||
.scroll(time(query.getScroll()))
|
||||
.scrollSize(query.getScrollSize());
|
||||
|
||||
if (query.getRouting() != null) {
|
||||
dqb.routing(query.getRouting());
|
||||
} else if (StringUtils.hasText(routing)) {
|
||||
dqb.routing(routing);
|
||||
}
|
||||
|
||||
if (query.getQ() != null) {
|
||||
dqb.q(query.getQ())
|
||||
.analyzer(query.getAnalyzer())
|
||||
.analyzeWildcard(query.getAnalyzeWildcard())
|
||||
.defaultOperator(operator(query.getDefaultOperator()))
|
||||
.df(query.getDf())
|
||||
.lenient(query.getLenient());
|
||||
}
|
||||
|
||||
if (query.getExpandWildcards() != null && !query.getExpandWildcards().isEmpty()) {
|
||||
dqb.expandWildcards(expandWildcards(query.getExpandWildcards()));
|
||||
}
|
||||
if (query.getStats() != null && !query.getStats().isEmpty()) {
|
||||
dqb.stats(query.getStats());
|
||||
}
|
||||
if (query.getSlices() != null) {
|
||||
dqb.slices(sb -> sb.value(query.getSlices()));
|
||||
}
|
||||
if (query.getSort() != null) {
|
||||
ElasticsearchPersistentEntity<?> persistentEntity = getPersistentEntity(clazz);
|
||||
List<SortOptions> sortOptions = getSortOptions(query.getSort(), persistentEntity);
|
||||
|
||||
if (!sortOptions.isEmpty()) {
|
||||
dqb.sort(
|
||||
sortOptions.stream()
|
||||
.map(sortOption -> {
|
||||
String order = "asc";
|
||||
var sortField = sortOption.field();
|
||||
if (sortField.order() != null) {
|
||||
order = sortField.order().jsonValue();
|
||||
}
|
||||
|
||||
return sortField.field() + ":" + order;
|
||||
})
|
||||
.collect(Collectors.toList())
|
||||
);
|
||||
}
|
||||
}
|
||||
dqb.allowNoIndices(query.getAllowNoIndices())
|
||||
.conflicts(conflicts(query.getConflicts()))
|
||||
.ignoreUnavailable(query.getIgnoreUnavailable())
|
||||
.preference(query.getPreference())
|
||||
.requestCache(query.getRequestCache())
|
||||
.searchType(searchType(query.getSearchType()))
|
||||
.searchTimeout(time(query.getSearchTimeout()))
|
||||
.terminateAfter(query.getTerminateAfter())
|
||||
.timeout(time(query.getTimeout()))
|
||||
.version(query.getVersion());
|
||||
|
||||
return dqb;
|
||||
});
|
||||
}
|
||||
|
||||
public UpdateRequest<Document, ?> documentUpdateRequest(UpdateQuery query, IndexCoordinates index,
|
||||
@Nullable RefreshPolicy refreshPolicy, @Nullable String routing) {
|
||||
|
||||
|
@ -18,6 +18,7 @@ package org.springframework.data.elasticsearch.client.elc;
|
||||
import co.elastic.clients.elasticsearch._types.*;
|
||||
import co.elastic.clients.elasticsearch._types.mapping.FieldType;
|
||||
import co.elastic.clients.elasticsearch._types.mapping.TypeMapping;
|
||||
import co.elastic.clients.elasticsearch._types.query_dsl.Operator;
|
||||
import co.elastic.clients.elasticsearch.core.search.BoundaryScanner;
|
||||
import co.elastic.clients.elasticsearch.core.search.HighlighterEncoder;
|
||||
import co.elastic.clients.elasticsearch.core.search.HighlighterFragmenter;
|
||||
@ -46,6 +47,8 @@ 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.query.types.ConflictsType;
|
||||
import org.springframework.data.elasticsearch.core.query.types.OperatorType;
|
||||
import org.springframework.data.elasticsearch.core.reindex.ReindexRequest;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
@ -500,4 +503,26 @@ final class TypeUtils {
|
||||
});
|
||||
return mappedParams;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a spring-data-elasticsearch operator to an Elasticsearch operator.
|
||||
*
|
||||
* @param operator spring-data-elasticsearch operator.
|
||||
* @return an Elasticsearch Operator.
|
||||
*/
|
||||
@Nullable
|
||||
static Operator operator(@Nullable OperatorType operator) {
|
||||
return operator != null ? Operator.valueOf(operator.name()) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a spring-data-elasticsearch {@literal conflicts} to an Elasticsearch {@literal conflicts}.
|
||||
*
|
||||
* @param conflicts spring-data-elasticsearch {@literal conflicts}.
|
||||
* @return an Elasticsearch {@literal conflicts}.
|
||||
*/
|
||||
@Nullable
|
||||
static Conflicts conflicts(@Nullable ConflictsType conflicts) {
|
||||
return conflicts != null ? Conflicts.valueOf(conflicts.name()) : null;
|
||||
}
|
||||
}
|
||||
|
@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.core;
|
||||
|
||||
import org.springframework.data.elasticsearch.core.query.DeleteQuery;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.core.publisher.Sinks;
|
||||
@ -411,6 +412,11 @@ abstract public class AbstractReactiveElasticsearchTemplate
|
||||
public Mono<ByQueryResponse> delete(Query query, Class<?> entityType) {
|
||||
return delete(query, entityType, getIndexCoordinatesFor(entityType));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<ByQueryResponse> delete(DeleteQuery query, Class<?> entityType) {
|
||||
return delete(query, entityType, getIndexCoordinatesFor(entityType));
|
||||
}
|
||||
// endregion
|
||||
|
||||
// region SearchDocument
|
||||
|
@ -21,6 +21,7 @@ import java.util.List;
|
||||
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.DeleteQuery;
|
||||
import org.springframework.data.elasticsearch.core.query.IndexQuery;
|
||||
import org.springframework.data.elasticsearch.core.query.Query;
|
||||
import org.springframework.data.elasticsearch.core.query.UpdateQuery;
|
||||
@ -279,9 +280,21 @@ public interface DocumentOperations {
|
||||
* {@link org.springframework.data.elasticsearch.annotations.Document}
|
||||
* @return response with detailed information
|
||||
* @since 4.1
|
||||
* @deprecated since 5.3.0, use {@link #delete(DeleteQuery, Class)}
|
||||
*/
|
||||
ByQueryResponse delete(Query query, Class<?> clazz);
|
||||
|
||||
/**
|
||||
* Delete all records matching the query.
|
||||
*
|
||||
* @param query query defining the objects
|
||||
* @param clazz The entity class must be annotated with
|
||||
* {@link org.springframework.data.elasticsearch.annotations.Document}
|
||||
* @return response with detailed information
|
||||
* @since 5.3
|
||||
*/
|
||||
ByQueryResponse delete(DeleteQuery query, Class<?> clazz);
|
||||
|
||||
/**
|
||||
* Delete all records matching the query.
|
||||
*
|
||||
@ -290,9 +303,22 @@ public interface DocumentOperations {
|
||||
* {@link org.springframework.data.elasticsearch.annotations.Document}
|
||||
* @param index the index from which to delete
|
||||
* @return response with detailed information
|
||||
* @deprecated since 5.3.0, use {@link #delete(DeleteQuery, Class, IndexCoordinates)}
|
||||
*/
|
||||
ByQueryResponse delete(Query query, Class<?> clazz, IndexCoordinates index);
|
||||
|
||||
/**
|
||||
* Delete all records matching the query.
|
||||
*
|
||||
* @param query query defining the objects
|
||||
* @param clazz The entity class must be annotated with
|
||||
* {@link org.springframework.data.elasticsearch.annotations.Document}
|
||||
* @param index the index from which to delete
|
||||
* @return response with detailed information
|
||||
* @since 5.3
|
||||
*/
|
||||
ByQueryResponse delete(DeleteQuery query, Class<?> clazz, IndexCoordinates index);
|
||||
|
||||
/**
|
||||
* Partially update a document by the given entity.
|
||||
*
|
||||
|
@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.core;
|
||||
|
||||
import org.springframework.data.elasticsearch.core.query.DeleteQuery;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
@ -331,9 +332,20 @@ public interface ReactiveDocumentOperations {
|
||||
* @param query must not be {@literal null}.
|
||||
* @param entityType must not be {@literal null}.
|
||||
* @return a {@link Mono} emitting the number of the removed documents.
|
||||
* @deprecated since 5.3.0, use {@link #delete(DeleteQuery, Class)}
|
||||
*/
|
||||
Mono<ByQueryResponse> delete(Query query, Class<?> entityType);
|
||||
|
||||
/**
|
||||
* Delete the documents matching the given {@link Query} extracting index from entity metadata.
|
||||
*
|
||||
* @param query must not be {@literal null}.
|
||||
* @param entityType must not be {@literal null}.
|
||||
* @return a {@link Mono} emitting the number of the removed documents.
|
||||
* @since 5.3
|
||||
*/
|
||||
Mono<ByQueryResponse> delete(DeleteQuery query, Class<?> entityType);
|
||||
|
||||
/**
|
||||
* Delete the documents matching the given {@link Query} extracting index from entity metadata.
|
||||
*
|
||||
@ -341,9 +353,21 @@ public interface ReactiveDocumentOperations {
|
||||
* @param entityType must not be {@literal null}.
|
||||
* @param index the target index, must not be {@literal null}
|
||||
* @return a {@link Mono} emitting the number of the removed documents.
|
||||
* @deprecated since 5.3.0, use {@link #delete(DeleteQuery, Class, IndexCoordinates)}
|
||||
*/
|
||||
Mono<ByQueryResponse> delete(Query query, Class<?> entityType, IndexCoordinates index);
|
||||
|
||||
/**
|
||||
* Delete the documents matching the given {@link Query} extracting index from entity metadata.
|
||||
*
|
||||
* @param query must not be {@literal null}.
|
||||
* @param entityType must not be {@literal null}.
|
||||
* @param index the target index, must not be {@literal null}
|
||||
* @return a {@link Mono} emitting the number of the removed documents.
|
||||
* @since 5.3
|
||||
*/
|
||||
Mono<ByQueryResponse> delete(DeleteQuery query, Class<?> entityType, IndexCoordinates index);
|
||||
|
||||
/**
|
||||
* Partial update of the document.
|
||||
*
|
||||
|
@ -0,0 +1,736 @@
|
||||
/*
|
||||
* Copyright 2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.core.query;
|
||||
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.elasticsearch.core.query.Query.SearchType;
|
||||
import org.springframework.data.elasticsearch.core.query.types.ConflictsType;
|
||||
import org.springframework.data.elasticsearch.core.query.types.OperatorType;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Defines a delete request.
|
||||
*
|
||||
* @author Aouichaoui Youssef
|
||||
* @see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-delete-by-query.html">docs</a>
|
||||
*/
|
||||
public class DeleteQuery {
|
||||
// For Lucene query
|
||||
/**
|
||||
* Query in the Lucene query string syntax.
|
||||
*/
|
||||
@Nullable
|
||||
private final String q;
|
||||
|
||||
/**
|
||||
* If true, wildcard and prefix queries are analyzed. Defaults to false.
|
||||
* This parameter can only be used when the lucene query {@code q} parameter is specified.
|
||||
*/
|
||||
@Nullable
|
||||
private final Boolean analyzeWildcard;
|
||||
|
||||
/**
|
||||
* Analyzer to use for the query string.
|
||||
* This parameter can only be used when the lucene query {@code q} parameter is specified.
|
||||
*/
|
||||
@Nullable
|
||||
private final String analyzer;
|
||||
|
||||
/**
|
||||
* The default operator for a query string query: {@literal AND} or {@literal OR}. Defaults to {@literal OR}.
|
||||
* This parameter can only be used when the lucene query {@code q} parameter is specified.
|
||||
*/
|
||||
@Nullable
|
||||
private final OperatorType defaultOperator;
|
||||
|
||||
/**
|
||||
* Field to be used as the default when no field prefix is specified in the query string.
|
||||
* This parameter can only be used when the lucene query {@code q} parameter is specified.
|
||||
* <p>
|
||||
* e.g: {@code {"query":{"prefix":{"user.name":{"value":"es"}}}} }
|
||||
*/
|
||||
@Nullable
|
||||
private final String df;
|
||||
|
||||
/**
|
||||
* If a query contains errors related to the format of the data being entered, they will be disregarded unless specified otherwise.
|
||||
* By default, this feature is turned off.
|
||||
*/
|
||||
@Nullable
|
||||
private final Boolean lenient;
|
||||
|
||||
// For ES query
|
||||
|
||||
/**
|
||||
* An error will occur if the condition is {@code false} and any of the following are true: a wildcard expression,
|
||||
* an index alias, or the {@literal _all value} only targets missing or closed indices.
|
||||
* By default, this is set to {@code true}.
|
||||
*/
|
||||
@Nullable
|
||||
private final Boolean allowNoIndices;
|
||||
|
||||
/**
|
||||
* Define the types of conflicts that occur when a query encounters version conflicts: abort or proceed.
|
||||
* Defaults to abort.
|
||||
*/
|
||||
@Nullable
|
||||
private final ConflictsType conflicts;
|
||||
|
||||
/**
|
||||
* Type of index that wildcard patterns can match.
|
||||
* Defaults to {@literal open}.
|
||||
*/
|
||||
@Nullable
|
||||
private final EnumSet<IndicesOptions.WildcardStates> expandWildcards;
|
||||
|
||||
/**
|
||||
* An error occurs if it is directed at an index that is missing or closed when it is {@code false}.
|
||||
* By default, this is set to {@code false}.
|
||||
*/
|
||||
@Nullable
|
||||
private final Boolean ignoreUnavailable;
|
||||
|
||||
/**
|
||||
* Maximum number of documents to process.
|
||||
* Defaults to all documents.
|
||||
*/
|
||||
@Nullable
|
||||
private final Long maxDocs;
|
||||
|
||||
/**
|
||||
* Specifies the node or shard the operation should be performed on.
|
||||
*/
|
||||
@Nullable
|
||||
private final String preference;
|
||||
|
||||
/**
|
||||
* Use the request cache when it is {@code true}.
|
||||
* By default, use the index-level setting.
|
||||
*/
|
||||
@Nullable
|
||||
private final Boolean requestCache;
|
||||
|
||||
/**
|
||||
* Refreshes all shards involved in the deleting by query after the request completes when it is {@code true}.
|
||||
* By default, this is set to {@code false}.
|
||||
*/
|
||||
@Nullable
|
||||
private final Boolean refresh;
|
||||
|
||||
/**
|
||||
* Limited this request to a certain number of sub-requests per second.
|
||||
* By default, this is set to {@code -1} (no throttle).
|
||||
*/
|
||||
@Nullable
|
||||
private final Float requestsPerSecond;
|
||||
|
||||
/**
|
||||
* Custom value used to route operations to a specific shard.
|
||||
*/
|
||||
@Nullable
|
||||
private final String routing;
|
||||
|
||||
/**
|
||||
* Period to retain the search context for scrolling.
|
||||
*/
|
||||
@Nullable
|
||||
private final Duration scroll;
|
||||
|
||||
/**
|
||||
* Size of the scroll request that powers the operation.
|
||||
* By default, this is set to {@code 1000}.
|
||||
*/
|
||||
@Nullable
|
||||
private final Long scrollSize;
|
||||
|
||||
/**
|
||||
* The type of the search operation.
|
||||
*/
|
||||
@Nullable
|
||||
private final SearchType searchType;
|
||||
|
||||
/**
|
||||
* Explicit timeout for each search request.
|
||||
* By default, this is set to no timeout.
|
||||
*/
|
||||
@Nullable
|
||||
private final Duration searchTimeout;
|
||||
|
||||
/**
|
||||
* The number of slices this task should be divided into.
|
||||
* By default, this is set to {@code 1} meaning the task isn’t sliced into subtasks.
|
||||
*/
|
||||
@Nullable
|
||||
private final Integer slices;
|
||||
|
||||
/**
|
||||
* Sort search results in a specific order.
|
||||
*/
|
||||
@Nullable
|
||||
private final Sort sort;
|
||||
|
||||
/**
|
||||
* Specific {@code tag} of the request for logging and statistical purposes.
|
||||
*/
|
||||
@Nullable
|
||||
private final List<String> stats;
|
||||
|
||||
/**
|
||||
* The Maximum number of documents that can be collected for each shard.
|
||||
* If a query exceeds this limit, Elasticsearch will stop the query.
|
||||
*/
|
||||
@Nullable
|
||||
private final Long terminateAfter;
|
||||
|
||||
/**
|
||||
* Period each deletion request waits for active shards.
|
||||
* By default, this is set to {@code 1m} (one minute).
|
||||
*/
|
||||
@Nullable
|
||||
private final Duration timeout;
|
||||
|
||||
/**
|
||||
* Returns the document version as part of a hit.
|
||||
*/
|
||||
@Nullable
|
||||
private final Boolean version;
|
||||
|
||||
// Body
|
||||
/**
|
||||
* Query that specifies the documents to delete.
|
||||
*/
|
||||
private final Query query;
|
||||
|
||||
public static Builder builder(Query query) {
|
||||
return new Builder(query);
|
||||
}
|
||||
|
||||
private DeleteQuery(Builder builder) {
|
||||
this.q = builder.luceneQuery;
|
||||
this.analyzeWildcard = builder.analyzeWildcard;
|
||||
this.analyzer = builder.analyzer;
|
||||
this.defaultOperator = builder.defaultOperator;
|
||||
this.df = builder.defaultField;
|
||||
this.lenient = builder.lenient;
|
||||
|
||||
this.allowNoIndices = builder.allowNoIndices;
|
||||
this.conflicts = builder.conflicts;
|
||||
this.expandWildcards = builder.expandWildcards;
|
||||
this.ignoreUnavailable = builder.ignoreUnavailable;
|
||||
this.maxDocs = builder.maxDocs;
|
||||
this.preference = builder.preference;
|
||||
this.requestCache = builder.requestCache;
|
||||
this.refresh = builder.refresh;
|
||||
this.requestsPerSecond = builder.requestsPerSecond;
|
||||
this.routing = builder.routing;
|
||||
this.scroll = builder.scrollTime;
|
||||
this.scrollSize = builder.scrollSize;
|
||||
this.searchType = builder.searchType;
|
||||
this.searchTimeout = builder.searchTimeout;
|
||||
this.slices = builder.slices;
|
||||
this.sort = builder.sort;
|
||||
this.stats = builder.stats;
|
||||
this.terminateAfter = builder.terminateAfter;
|
||||
this.timeout = builder.timeout;
|
||||
this.version = builder.version;
|
||||
|
||||
this.query = builder.query;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getQ() {
|
||||
return q;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Boolean getAnalyzeWildcard() {
|
||||
return analyzeWildcard;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getAnalyzer() {
|
||||
return analyzer;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public OperatorType getDefaultOperator() {
|
||||
return defaultOperator;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getDf() {
|
||||
return df;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Boolean getLenient() {
|
||||
return lenient;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Boolean getAllowNoIndices() {
|
||||
return allowNoIndices;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public ConflictsType getConflicts() {
|
||||
return conflicts;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public EnumSet<IndicesOptions.WildcardStates> getExpandWildcards() {
|
||||
return expandWildcards;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Boolean getIgnoreUnavailable() {
|
||||
return ignoreUnavailable;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Long getMaxDocs() {
|
||||
return maxDocs;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getPreference() {
|
||||
return preference;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Boolean getRequestCache() {
|
||||
return requestCache;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Boolean getRefresh() {
|
||||
return refresh;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Float getRequestsPerSecond() {
|
||||
return requestsPerSecond;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getRouting() {
|
||||
return routing;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Duration getScroll() {
|
||||
return scroll;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Long getScrollSize() {
|
||||
return scrollSize;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public SearchType getSearchType() {
|
||||
return searchType;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Duration getSearchTimeout() {
|
||||
return searchTimeout;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Integer getSlices() {
|
||||
return slices;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Sort getSort() {
|
||||
return sort;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public List<String> getStats() {
|
||||
return stats;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Long getTerminateAfter() {
|
||||
return terminateAfter;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Duration getTimeout() {
|
||||
return timeout;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Boolean getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Query getQuery() {
|
||||
return query;
|
||||
}
|
||||
|
||||
public static final class Builder {
|
||||
// For Lucene query
|
||||
@Nullable
|
||||
private String luceneQuery;
|
||||
@Nullable
|
||||
private Boolean analyzeWildcard;
|
||||
@Nullable
|
||||
private String analyzer;
|
||||
@Nullable
|
||||
private OperatorType defaultOperator;
|
||||
@Nullable
|
||||
private String defaultField;
|
||||
@Nullable
|
||||
private Boolean lenient;
|
||||
|
||||
// For ES query
|
||||
@Nullable
|
||||
private Boolean allowNoIndices;
|
||||
@Nullable
|
||||
private ConflictsType conflicts;
|
||||
@Nullable
|
||||
private EnumSet<IndicesOptions.WildcardStates> expandWildcards;
|
||||
@Nullable
|
||||
private Boolean ignoreUnavailable;
|
||||
@Nullable
|
||||
private Long maxDocs;
|
||||
@Nullable
|
||||
private String preference;
|
||||
@Nullable
|
||||
private Boolean requestCache;
|
||||
@Nullable
|
||||
private Boolean refresh;
|
||||
@Nullable
|
||||
private Float requestsPerSecond;
|
||||
@Nullable
|
||||
private String routing;
|
||||
@Nullable
|
||||
private Duration scrollTime;
|
||||
@Nullable
|
||||
private Long scrollSize;
|
||||
@Nullable
|
||||
private SearchType searchType;
|
||||
@Nullable
|
||||
private Duration searchTimeout;
|
||||
@Nullable
|
||||
private Integer slices;
|
||||
@Nullable
|
||||
private Sort sort;
|
||||
@Nullable
|
||||
private List<String> stats;
|
||||
@Nullable
|
||||
private Long terminateAfter;
|
||||
@Nullable
|
||||
private Duration timeout;
|
||||
@Nullable
|
||||
private Boolean version;
|
||||
|
||||
// Body
|
||||
private final Query query;
|
||||
|
||||
private Builder(Query query) {
|
||||
Assert.notNull(query, "query must not be null");
|
||||
|
||||
this.query = query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Query in the Lucene query string syntax.
|
||||
*/
|
||||
public Builder withLuceneQuery(@Nullable String luceneQuery) {
|
||||
this.luceneQuery = luceneQuery;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* If true, wildcard and prefix queries are analyzed. Defaults to false.
|
||||
* This parameter can only be used when the lucene query {@code q} parameter is specified.
|
||||
*/
|
||||
public Builder withAnalyzeWildcard(@Nullable Boolean analyzeWildcard) {
|
||||
this.analyzeWildcard = analyzeWildcard;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Analyzer to use for the query string.
|
||||
* This parameter can only be used when the lucene query {@code q} parameter is specified.
|
||||
*/
|
||||
public Builder withAnalyzer(@Nullable String analyzer) {
|
||||
this.analyzer = analyzer;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* The default operator for a query string query: {@literal AND} or {@literal OR}. Defaults to {@literal OR}.
|
||||
* This parameter can only be used when the lucene query {@code q} parameter is specified.
|
||||
*/
|
||||
public Builder withDefaultOperator(@Nullable OperatorType defaultOperator) {
|
||||
this.defaultOperator = defaultOperator;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Field to be used as the default when no field prefix is specified in the query string.
|
||||
* This parameter can only be used when the lucene query {@code q} parameter is specified.
|
||||
* <p>
|
||||
* e.g: {@code {"query":{"prefix":{"user.name":{"value":"es"}}}} }
|
||||
*/
|
||||
public Builder withDefaultField(@Nullable String defaultField) {
|
||||
this.defaultField = defaultField;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* If a query contains errors related to the format of the data being entered, they will be disregarded unless specified otherwise.
|
||||
* By default, this feature is turned off.
|
||||
*/
|
||||
public Builder withLenient(@Nullable Boolean lenient) {
|
||||
this.lenient = lenient;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* An error will occur if the condition is {@code false} and any of the following are true: a wildcard expression,
|
||||
* an index alias, or the {@literal _all value} only targets missing or closed indices.
|
||||
* By default, this is set to {@code true}.
|
||||
*/
|
||||
public Builder withAllowNoIndices(@Nullable Boolean allowNoIndices) {
|
||||
this.allowNoIndices = allowNoIndices;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the types of conflicts that occur when a query encounters version conflicts: abort or proceed.
|
||||
* Defaults to abort.
|
||||
*/
|
||||
public Builder withConflicts(@Nullable ConflictsType conflicts) {
|
||||
this.conflicts = conflicts;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Type of index that wildcard patterns can match.
|
||||
* Defaults to {@literal open}.
|
||||
*/
|
||||
public Builder setExpandWildcards(@Nullable EnumSet<IndicesOptions.WildcardStates> expandWildcards) {
|
||||
this.expandWildcards = expandWildcards;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* An error occurs if it is directed at an index that is missing or closed when it is {@code false}.
|
||||
* By default, this is set to {@code false}.
|
||||
*/
|
||||
public Builder withIgnoreUnavailable(@Nullable Boolean ignoreUnavailable) {
|
||||
this.ignoreUnavailable = ignoreUnavailable;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Maximum number of documents to process.
|
||||
* Defaults to all documents.
|
||||
*/
|
||||
public Builder withMaxDocs(@Nullable Long maxDocs) {
|
||||
this.maxDocs = maxDocs;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the node or shard the operation should be performed on.
|
||||
*/
|
||||
public Builder withPreference(@Nullable String preference) {
|
||||
this.preference = preference;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the request cache when it is {@code true}.
|
||||
* By default, use the index-level setting.
|
||||
*/
|
||||
public Builder withRequestCache(@Nullable Boolean requestCache) {
|
||||
this.requestCache = requestCache;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes all shards involved in the deleting by query after the request completes when it is {@code true}.
|
||||
* By default, this is set to {@code false}.
|
||||
*/
|
||||
public Builder withRefresh(@Nullable Boolean refresh) {
|
||||
this.refresh = refresh;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Limited this request to a certain number of sub-requests per second.
|
||||
* By default, this is set to {@code -1} (no throttle).
|
||||
*/
|
||||
public Builder withRequestsPerSecond(@Nullable Float requestsPerSecond) {
|
||||
this.requestsPerSecond = requestsPerSecond;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom value used to route operations to a specific shard.
|
||||
*/
|
||||
public Builder withRouting(@Nullable String routing) {
|
||||
this.routing = routing;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Period to retain the search context for scrolling.
|
||||
*/
|
||||
public Builder withScrollTime(@Nullable Duration scrollTime) {
|
||||
this.scrollTime = scrollTime;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Size of the scroll request that powers the operation.
|
||||
* By default, this is set to {@code 1000}.
|
||||
*/
|
||||
public Builder withScrollSize(@Nullable Long scrollSize) {
|
||||
this.scrollSize = scrollSize;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* The type of the search operation.
|
||||
*/
|
||||
public Builder withSearchType(@Nullable SearchType searchType) {
|
||||
this.searchType = searchType;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Explicit timeout for each search request.
|
||||
* By default, this is set to no timeout.
|
||||
*/
|
||||
public Builder withSearchTimeout(@Nullable Duration searchTimeout) {
|
||||
this.searchTimeout = searchTimeout;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* The number of slices this task should be divided into.
|
||||
* By default, this is set to {@code 1} meaning the task isn’t sliced into subtasks.
|
||||
*/
|
||||
public Builder withSlices(@Nullable Integer slices) {
|
||||
this.slices = slices;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort search results in a specific order.
|
||||
*/
|
||||
public Builder withSort(@Nullable Sort sort) {
|
||||
this.sort = sort;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specific {@code tag} of the request for logging and statistical purposes.
|
||||
*/
|
||||
public Builder withStats(@Nullable List<String> stats) {
|
||||
this.stats = stats;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* The Maximum number of documents that can be collected for each shard.
|
||||
* If a query exceeds this limit, Elasticsearch will stop the query.
|
||||
*/
|
||||
public Builder withTerminateAfter(@Nullable Long terminateAfter) {
|
||||
this.terminateAfter = terminateAfter;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Period each deletion request waits for active shards.
|
||||
* By default, this is set to {@code 1m} (one minute).
|
||||
*/
|
||||
public Builder withTimeout(@Nullable Duration timeout) {
|
||||
this.timeout = timeout;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the document version as part of a hit.
|
||||
*/
|
||||
public Builder withVersion(@Nullable Boolean version) {
|
||||
this.version = version;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public DeleteQuery build() {
|
||||
if (luceneQuery == null) {
|
||||
if (defaultField != null) {
|
||||
throw new IllegalArgumentException("When defining the df parameter, you must include the Lucene query.");
|
||||
}
|
||||
if (analyzer != null) {
|
||||
throw new IllegalArgumentException("When defining the analyzer parameter, you must include the Lucene query.");
|
||||
}
|
||||
if (analyzeWildcard != null) {
|
||||
throw new IllegalArgumentException("When defining the analyzeWildcard parameter, you must include the Lucene query.");
|
||||
}
|
||||
if (defaultOperator != null) {
|
||||
throw new IllegalArgumentException("When defining the defaultOperator parameter, you must include the Lucene query.");
|
||||
}
|
||||
if (lenient != null) {
|
||||
throw new IllegalArgumentException("When defining the lenient parameter, you must include the Lucene query.");
|
||||
}
|
||||
}
|
||||
|
||||
return new DeleteQuery(this);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright 2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.core.query.types;
|
||||
|
||||
/**
|
||||
* Define the types of conflicts that occur when a query encounters version conflicts.
|
||||
*
|
||||
* @author Aouichaoui Youssef
|
||||
*/
|
||||
public enum ConflictsType {
|
||||
Abort, Proceed
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright 2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.core.query.types;
|
||||
|
||||
/**
|
||||
* Define the default operator for a query string query.
|
||||
*
|
||||
* @author Aouichaoui Youssef
|
||||
*/
|
||||
public enum OperatorType {
|
||||
And, Or
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
@org.springframework.lang.NonNullApi
|
||||
@org.springframework.lang.NonNullFields
|
||||
package org.springframework.data.elasticsearch.core.query.types;
|
@ -3776,6 +3776,44 @@ public abstract class ElasticsearchIntegrationTests {
|
||||
}).isInstanceOf(VersionConflictException.class);
|
||||
}
|
||||
|
||||
@Test // GH-2865
|
||||
public void shouldDeleteDocumentForGivenQueryUsingParameters() {
|
||||
// Given
|
||||
String documentId = nextIdAsString();
|
||||
SampleEntity sampleEntity = SampleEntity.builder().id(documentId).message("some message")
|
||||
.version(System.currentTimeMillis()).build();
|
||||
|
||||
IndexQuery indexQuery = getIndexQuery(sampleEntity);
|
||||
String indexName = indexNameProvider.indexName();
|
||||
|
||||
operations.index(indexQuery, IndexCoordinates.of(indexName));
|
||||
|
||||
// When
|
||||
final Query query = getTermQuery("id", documentId);
|
||||
final DeleteQuery deleteQuery = DeleteQuery.builder(query).withSlices(2).build();
|
||||
ByQueryResponse result = operations.delete(deleteQuery, SampleEntity.class, IndexCoordinates.of(indexName));
|
||||
|
||||
// Then
|
||||
assertThat(result.getDeleted()).isEqualTo(1);
|
||||
SearchHits<SampleEntity> searchHits = operations.search(query, SampleEntity.class,
|
||||
IndexCoordinates.of(indexName));
|
||||
assertThat(searchHits.getTotalHits()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldDeleteDocumentForGivenQueryAndUnavailableIndex() {
|
||||
// Given
|
||||
String indexName = UUID.randomUUID().toString();
|
||||
|
||||
// When
|
||||
final Query query = operations.matchAllQuery();
|
||||
final DeleteQuery deleteQuery = DeleteQuery.builder(query).withIgnoreUnavailable(true).build();
|
||||
ByQueryResponse result = operations.delete(deleteQuery, SampleEntity.class, IndexCoordinates.of(indexName));
|
||||
|
||||
// Then
|
||||
assertThat(result.getDeleted()).isEqualTo(0);
|
||||
}
|
||||
|
||||
// region entities
|
||||
@Document(indexName = "#{@indexNameProvider.indexName()}")
|
||||
@Setting(shards = 1, replicas = 0, refreshInterval = "-1")
|
||||
|
Loading…
x
Reference in New Issue
Block a user