Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import org.elasticsearch.ExceptionsHelper;
import org.elasticsearch.action.FailedNodeException;
import org.elasticsearch.action.ShardOperationFailedException;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.support.broadcast.BaseBroadcastResponse;
import org.elasticsearch.action.support.nodes.BaseNodeResponse;
import org.elasticsearch.action.support.nodes.BaseNodesResponse;
Expand Down Expand Up @@ -47,6 +48,7 @@ public class RestActions {
public static final ParseField SKIPPED_FIELD = new ParseField("skipped");
public static final ParseField FAILED_FIELD = new ParseField("failed");
public static final ParseField FAILURES_FIELD = new ParseField("failures");
public static final ParseField PROJECT_ROUTING = new ParseField("project_routing");

public static long parseVersion(RestRequest request) {
if (request.hasParam("version")) {
Expand Down Expand Up @@ -260,10 +262,14 @@ public RestResponse buildResponse(NodesResponse response, XContentBuilder builde

}

public static QueryBuilder getQueryContent(XContentParser parser) {
return getQueryContent(parser, null);
}

/**
* Parses a top level query including the query element that wraps it
*/
public static QueryBuilder getQueryContent(XContentParser parser) {
public static QueryBuilder getQueryContent(XContentParser parser, SearchRequest searchRequest) {
try {
QueryBuilder queryBuilder = null;
XContentParser.Token first = parser.nextToken();
Expand All @@ -281,6 +287,9 @@ public static QueryBuilder getQueryContent(XContentParser parser) {
String currentName = parser.currentName();
if ("query".equals(currentName)) {
queryBuilder = parseTopLevelQuery(parser);
} else if (PROJECT_ROUTING.match(currentName, parser.getDeprecationHandler()) && searchRequest != null) {
parser.nextToken();
searchRequest.setProjectRouting(parser.text());
} else {
throw new ParsingException(parser.getTokenLocation(), "request does not support [" + parser.currentName() + "]");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.support.IndicesOptions;
import org.elasticsearch.client.internal.node.NodeClient;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.Table;
Expand All @@ -25,24 +26,31 @@
import org.elasticsearch.rest.action.RestActions;
import org.elasticsearch.rest.action.RestResponseListener;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.crossproject.CrossProjectModeDecider;

import java.io.IOException;
import java.util.List;

import static org.elasticsearch.rest.RestRequest.Method.GET;
import static org.elasticsearch.rest.RestRequest.Method.POST;

@ServerlessScope(Scope.PUBLIC)
public class RestCountAction extends AbstractCatAction {

private final Settings settings;
private final CrossProjectModeDecider crossProjectModeDecider;

public RestCountAction(Settings settings) {
this.settings = settings;
this.crossProjectModeDecider = new CrossProjectModeDecider(settings);
}

@Override
public List<Route> routes() {
return List.of(new Route(GET, "/_cat/count"), new Route(GET, "/_cat/count/{index}"));
return List.of(
new Route(GET, "/_cat/count"),
new Route(POST, "/_cat/count"),
new Route(GET, "/_cat/count/{index}"),
new Route(POST, "/_cat/count/{index}")
);
}

@Override
Expand All @@ -58,24 +66,25 @@ protected void documentation(StringBuilder sb) {

@Override
public RestChannelConsumer doCatRequest(final RestRequest request, final NodeClient client) {
if (settings != null && settings.getAsBoolean("serverless.cross_project.enabled", false)) {
// accept but drop project_routing param until fully supported
request.param("project_routing");
}

String[] indices = Strings.splitStringByCommaToArray(request.param("index"));
SearchRequest countRequest = new SearchRequest(indices);
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder().size(0).trackTotalHits(true);
countRequest.source(searchSourceBuilder);
if (crossProjectModeDecider.crossProjectEnabled() && countRequest.allowsCrossProject()) {
countRequest.indicesOptions(
IndicesOptions.builder().crossProjectModeOptions(new IndicesOptions.CrossProjectModeOptions(true)).build()
);
}
try {
request.withContentOrSourceParamParserOrNull(parser -> {
if (parser == null) {
QueryBuilder queryBuilder = RestActions.urlParamsToQueryBuilder(request);
if (queryBuilder != null) {
// since there is no request body, no need to pass in countRequest to handle project_routing param
searchSourceBuilder.query(queryBuilder);
}
} else {
searchSourceBuilder.query(RestActions.getQueryContent(parser));
searchSourceBuilder.query(RestActions.getQueryContent(parser, countRequest));
}
});
} catch (IOException e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import org.elasticsearch.rest.action.RestActions;
import org.elasticsearch.rest.action.RestBuilderListener;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.crossproject.CrossProjectModeDecider;
import org.elasticsearch.xcontent.XContentBuilder;

import java.io.IOException;
Expand All @@ -37,10 +38,10 @@
@ServerlessScope(Scope.PUBLIC)
public class RestCountAction extends BaseRestHandler {

private Settings settings;
private final CrossProjectModeDecider crossProjectModeDecider;

public RestCountAction(Settings settings) {
this.settings = settings;
this.crossProjectModeDecider = new CrossProjectModeDecider(settings);
}

@Override
Expand All @@ -60,23 +61,26 @@ public String getName() {

@Override
public RestChannelConsumer prepareRequest(final RestRequest request, final NodeClient client) throws IOException {
if (settings != null && settings.getAsBoolean("serverless.cross_project.enabled", false)) {
// accept but drop project_routing param until fully supported
request.param("project_routing");
SearchRequest countRequest = new SearchRequest(Strings.splitStringByCommaToArray(request.param("index")));
IndicesOptions indicesOptions = IndicesOptions.fromRequest(request, countRequest.indicesOptions());
if (crossProjectModeDecider.crossProjectEnabled() && countRequest.allowsCrossProject()) {
indicesOptions = IndicesOptions.builder(indicesOptions)
.crossProjectModeOptions(new IndicesOptions.CrossProjectModeOptions(true))
.build();
}
countRequest.indicesOptions(indicesOptions);

SearchRequest countRequest = new SearchRequest(Strings.splitStringByCommaToArray(request.param("index")));
countRequest.indicesOptions(IndicesOptions.fromRequest(request, countRequest.indicesOptions()));
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder().size(0).trackTotalHits(true);
countRequest.source(searchSourceBuilder);
request.withContentOrSourceParamParserOrNull(parser -> {
if (parser == null) {
QueryBuilder queryBuilder = RestActions.urlParamsToQueryBuilder(request);
if (queryBuilder != null) {
// since there is no request body, no need to pass in countRequest to handle project_routing param
searchSourceBuilder.query(queryBuilder);
}
} else {
searchSourceBuilder.query(RestActions.getQueryContent(parser));
searchSourceBuilder.query(RestActions.getQueryContent(parser, countRequest));
}
});
countRequest.routing(request.param("routing"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
package org.elasticsearch.rest.action;

import org.elasticsearch.action.ShardOperationFailedException;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.ShardSearchFailure;
import org.elasticsearch.cluster.metadata.IndexMetadata;
import org.elasticsearch.common.ParsingException;
Expand Down Expand Up @@ -42,6 +43,8 @@
import static java.util.Collections.emptyList;
import static org.elasticsearch.index.query.QueryStringQueryBuilder.DEFAULT_OPERATOR;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;

public class RestActionsTests extends ESTestCase {

Expand Down Expand Up @@ -233,6 +236,51 @@ public void testUrlParamsToQueryBuilderError() {
);
}

public void testParseWithProjectRouting() throws IOException {
QueryBuilder query = new MatchQueryBuilder("foo", "bar");
String requestBody1 = """
{
"query": _QUERY_,
"project_routing": "_alias:_origin"
}
""";
String requestBody2 = """
{
"project_routing": "_csp:aws AND (_region:us* OR _region:eu-west-1)",
"query": _QUERY_
}
""";

{
String requestBody = randomFrom(requestBody1, requestBody2).replaceFirst("_QUERY_", query.toString());
try (XContentParser parser = createParser(JsonXContent.jsonXContent, requestBody)) {
// if no SearchRequest passed in, an error should be thrown that project_routing is not supported for that endpoint
ParsingException e = expectThrows(ParsingException.class, () -> RestActions.getQueryContent(parser));
assertEquals(e.getMessage(), "request does not support [project_routing]");
}
}
{
SearchRequest searchRequest = new SearchRequest("index");
String requestBody = requestBody1.replaceFirst("_QUERY_", query.toString());
try (XContentParser parser = createParser(JsonXContent.jsonXContent, requestBody)) {
assertNull(searchRequest.getProjectRouting());
QueryBuilder actual = RestActions.getQueryContent(parser, searchRequest);
assertEquals(query, actual);
assertEquals(searchRequest.getProjectRouting(), "_alias:_origin");
}
}
{
String requestBody = requestBody2.replaceFirst("_QUERY_", query.toString());
try (XContentParser parser = createParser(JsonXContent.jsonXContent, requestBody)) {
SearchRequest searchRequest = new SearchRequest("index");
assertNull(searchRequest.getProjectRouting());
QueryBuilder actual = RestActions.getQueryContent(parser, searchRequest);
assertEquals(query, actual);
assertEquals(searchRequest.getProjectRouting(), "_csp:aws AND (_region:us* OR _region:eu-west-1)");
}
}
}

private static ShardSearchFailure createShardFailureParsingException(String nodeId, int shardId, String clusterAlias) {
String index = "index";
ParsingException ex = new ParsingException(0, 0, "error", new IllegalArgumentException("some bad argument"));
Expand Down