/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you 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
*
* http://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.elasticsearch.index.reindex;
import org.apache.lucene.util.IOUtils;
import org.elasticsearch.action.GenericAction;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.rest.RestRequest;
import org.elasticsearch.rest.action.search.RestSearchAction;
import java.io.IOException;
import java.util.Map;
import java.util.function.Consumer;
import static org.elasticsearch.index.reindex.AbstractBulkByScrollRequest.SIZE_ALL_MATCHES;
/**
* Rest handler for reindex actions that accepts a search request like Update-By-Query or Delete-By-Query
*/
public abstract class AbstractBulkByQueryRestHandler<
Request extends AbstractBulkByScrollRequest<Request>,
A extends GenericAction<Request, BulkByScrollResponse>> extends AbstractBaseReindexRestHandler<Request, A> {
protected AbstractBulkByQueryRestHandler(Settings settings, A action) {
super(settings, action);
}
protected void parseInternalRequest(Request internal, RestRequest restRequest,
Map<String, Consumer<Object>> bodyConsumers) throws IOException {
assert internal != null : "Request should not be null";
assert restRequest != null : "RestRequest should not be null";
SearchRequest searchRequest = internal.getSearchRequest();
int scrollSize = searchRequest.source().size();
searchRequest.source().size(SIZE_ALL_MATCHES);
restRequest.withContentOrSourceParamParserOrNull(parser -> {
XContentParser searchRequestParser = extractRequestSpecificFieldsAndReturnSearchCompatibleParser(parser, bodyConsumers);
/* searchRequestParser might be parser or it might be a new parser built from parser's contents. If it is parser then
* withContentOrSourceParamParserOrNull will close it for us but if it isn't then we should close it. Technically close on
* the generated parser probably is a noop but we should do the accounting just in case. It doesn't hurt to close twice but it
* really hurts not to close if by some miracle we have to. */
try {
RestSearchAction.parseSearchRequest(searchRequest, restRequest, searchRequestParser);
} finally {
IOUtils.close(searchRequestParser);
}
});
internal.setSize(searchRequest.source().size());
searchRequest.source().size(restRequest.paramAsInt("scroll_size", scrollSize));
String conflicts = restRequest.param("conflicts");
if (conflicts != null) {
internal.setConflicts(conflicts);
}
// Let the requester set search timeout. It is probably only going to be useful for testing but who knows.
if (restRequest.hasParam("search_timeout")) {
searchRequest.source().timeout(restRequest.paramAsTime("search_timeout", null));
}
}
/**
* We can't send parseSearchRequest REST content that it doesn't support
* so we will have to remove the content that is valid in addition to
* what it supports from the content first. This is a temporary hack and
* should get better when SearchRequest has full ObjectParser support
* then we can delegate and stuff.
*/
private XContentParser extractRequestSpecificFieldsAndReturnSearchCompatibleParser(XContentParser parser,
Map<String, Consumer<Object>> bodyConsumers) throws IOException {
if (parser == null) {
return parser;
}
try {
Map<String, Object> body = parser.map();
for (Map.Entry<String, Consumer<Object>> consumer : bodyConsumers.entrySet()) {
Object value = body.remove(consumer.getKey());
if (value != null) {
consumer.getValue().accept(value);
}
}
try (XContentBuilder builder = XContentFactory.contentBuilder(parser.contentType())) {
return parser.contentType().xContent().createParser(parser.getXContentRegistry(), builder.map(body).bytes());
}
} finally {
parser.close();
}
}
}