diff --git a/client/client-benchmark-noop-api-plugin/src/main/java/org/elasticsearch/plugin/noop/action/search/RestNoopSearchAction.java b/client/client-benchmark-noop-api-plugin/src/main/java/org/elasticsearch/plugin/noop/action/search/RestNoopSearchAction.java index d6619b33de6c2..a1e57b095df99 100644 --- a/client/client-benchmark-noop-api-plugin/src/main/java/org/elasticsearch/plugin/noop/action/search/RestNoopSearchAction.java +++ b/client/client-benchmark-noop-api-plugin/src/main/java/org/elasticsearch/plugin/noop/action/search/RestNoopSearchAction.java @@ -11,7 +11,7 @@ import org.elasticsearch.client.internal.node.NodeClient; import org.elasticsearch.rest.BaseRestHandler; import org.elasticsearch.rest.RestRequest; -import org.elasticsearch.rest.action.RestStatusToXContentListener; +import org.elasticsearch.rest.action.RestChunkedToXContentListener; import java.util.List; @@ -38,6 +38,6 @@ public String getName() { @Override public RestChannelConsumer prepareRequest(final RestRequest request, final NodeClient client) { SearchRequest searchRequest = new SearchRequest(); - return channel -> client.execute(NoopSearchAction.INSTANCE, searchRequest, new RestStatusToXContentListener<>(channel)); + return channel -> client.execute(NoopSearchAction.INSTANCE, searchRequest, new RestChunkedToXContentListener<>(channel)); } } diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/asyncsearch/AsyncSearchResponse.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/asyncsearch/AsyncSearchResponse.java index b3788eb6c9fe2..4ef47f7471e35 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/asyncsearch/AsyncSearchResponse.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/asyncsearch/AsyncSearchResponse.java @@ -10,6 +10,7 @@ import org.elasticsearch.ElasticsearchException; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.common.Strings; +import org.elasticsearch.common.xcontent.ChunkedToXContent; import org.elasticsearch.core.Nullable; import org.elasticsearch.xcontent.ConstructingObjectParser; import org.elasticsearch.xcontent.ParseField; @@ -133,7 +134,7 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws if (searchResponse != null) { builder.field("response"); - searchResponse.toXContent(builder, params); + ChunkedToXContent.wrapAsToXContent(searchResponse).toXContent(builder, params); } if (error != null) { builder.startObject("error"); diff --git a/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/SearchTemplateResponse.java b/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/SearchTemplateResponse.java index cbf6e3003ec3d..cb17ed039460d 100644 --- a/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/SearchTemplateResponse.java +++ b/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/SearchTemplateResponse.java @@ -13,6 +13,7 @@ import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.common.xcontent.ChunkedToXContent; import org.elasticsearch.common.xcontent.StatusToXContentObject; import org.elasticsearch.rest.RestStatus; import org.elasticsearch.xcontent.ParseField; @@ -101,16 +102,15 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws return builder; } - public XContentBuilder innerToXContent(XContentBuilder builder, Params params) throws IOException { + void innerToXContent(XContentBuilder builder, Params params) throws IOException { if (hasResponse()) { - response.innerToXContent(builder, params); + ChunkedToXContent.wrapAsToXContent(p -> response.innerToXContentChunked(p)).toXContent(builder, params); } else { // we can assume the template is always json as we convert it before compiling it try (InputStream stream = source.streamInput()) { builder.rawField(TEMPLATE_OUTPUT_FIELD.getPreferredName(), stream, XContentType.JSON); } } - return builder; } @Override diff --git a/server/src/main/java/org/elasticsearch/ElasticsearchException.java b/server/src/main/java/org/elasticsearch/ElasticsearchException.java index 1d9c4fb06ddad..abf13b2c23dbb 100644 --- a/server/src/main/java/org/elasticsearch/ElasticsearchException.java +++ b/server/src/main/java/org/elasticsearch/ElasticsearchException.java @@ -570,12 +570,11 @@ public static void generateThrowableXContent(XContentBuilder builder, Params par * This method is usually used when the {@link Exception} is rendered as a full XContent object, and its output can be parsed * by the {@link #failureFromXContent(XContentParser)} method. */ - public static void generateFailureXContent(XContentBuilder builder, Params params, @Nullable Exception e, boolean detailed) + public static XContentBuilder generateFailureXContent(XContentBuilder builder, Params params, @Nullable Exception e, boolean detailed) throws IOException { // No exception to render as an error if (e == null) { - builder.field(ERROR, "unknown"); - return; + return builder.field(ERROR, "unknown"); } // Render the exception with a simple message @@ -589,8 +588,7 @@ public static void generateFailureXContent(XContentBuilder builder, Params param } t = t.getCause(); } - builder.field(ERROR, message); - return; + return builder.field(ERROR, message); } // Render the exception with all details @@ -606,7 +604,7 @@ public static void generateFailureXContent(XContentBuilder builder, Params param builder.endArray(); } generateThrowableXContent(builder, params, e); - builder.endObject(); + return builder.endObject(); } /** diff --git a/server/src/main/java/org/elasticsearch/action/search/MultiSearchResponse.java b/server/src/main/java/org/elasticsearch/action/search/MultiSearchResponse.java index 041fdfeca76eb..02b2b9b99e68f 100644 --- a/server/src/main/java/org/elasticsearch/action/search/MultiSearchResponse.java +++ b/server/src/main/java/org/elasticsearch/action/search/MultiSearchResponse.java @@ -16,12 +16,14 @@ import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.io.stream.Writeable; +import org.elasticsearch.common.xcontent.ChunkedToXContent; +import org.elasticsearch.common.xcontent.ChunkedToXContentHelper; +import org.elasticsearch.common.xcontent.ChunkedToXContentObject; import org.elasticsearch.core.Nullable; import org.elasticsearch.core.TimeValue; import org.elasticsearch.xcontent.ConstructingObjectParser; import org.elasticsearch.xcontent.ParseField; -import org.elasticsearch.xcontent.ToXContentObject; -import org.elasticsearch.xcontent.XContentBuilder; +import org.elasticsearch.xcontent.ToXContent; import org.elasticsearch.xcontent.XContentParser; import org.elasticsearch.xcontent.XContentParser.Token; @@ -34,7 +36,7 @@ /** * A multi search response. */ -public class MultiSearchResponse extends ActionResponse implements Iterable, ToXContentObject { +public class MultiSearchResponse extends ActionResponse implements Iterable, ChunkedToXContentObject { private static final ParseField RESPONSES = new ParseField(Fields.RESPONSES); private static final ParseField TOOK_IN_MILLIS = new ParseField("took"); @@ -52,7 +54,7 @@ public class MultiSearchResponse extends ActionResponse implements Iterable toXContentChunked(ToXContent.Params params) { + if (isFailure()) { + return Iterators.concat( + ChunkedToXContentHelper.startObject(), + Iterators.single((b, p) -> ElasticsearchException.generateFailureXContent(b, p, Item.this.getFailure(), true)), + Iterators.single((b, p) -> b.field(Fields.STATUS, ExceptionsHelper.status(Item.this.getFailure()).getStatus())), + ChunkedToXContentHelper.endObject() + ); + } else { + return Iterators.concat( + ChunkedToXContentHelper.startObject(), + Item.this.getResponse().innerToXContentChunked(params), + Iterators.single((b, p) -> b.field(Fields.STATUS, Item.this.getResponse().status().getStatus())), + ChunkedToXContentHelper.endObject() + ); + } + } + /** * Is it a failed search? */ @@ -150,24 +171,14 @@ public void writeTo(StreamOutput out) throws IOException { } @Override - public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { - builder.startObject(); - builder.field("took", tookInMillis); - builder.startArray(Fields.RESPONSES); - for (Item item : items) { - builder.startObject(); - if (item.isFailure()) { - ElasticsearchException.generateFailureXContent(builder, params, item.getFailure(), true); - builder.field(Fields.STATUS, ExceptionsHelper.status(item.getFailure()).getStatus()); - } else { - item.getResponse().innerToXContent(builder, params); - builder.field(Fields.STATUS, item.getResponse().status().getStatus()); - } - builder.endObject(); - } - builder.endArray(); - builder.endObject(); - return builder; + public Iterator toXContentChunked(ToXContent.Params params) { + return Iterators.concat( + ChunkedToXContentHelper.startObject(), + Iterators.single((b, p) -> b.field("took", tookInMillis).startArray(Fields.RESPONSES)), + Iterators.flatMap(Iterators.forArray(items), item -> item.toXContentChunked(params)), + Iterators.single((b, p) -> b.endArray()), + ChunkedToXContentHelper.endObject() + ); } public static MultiSearchResponse fromXContext(XContentParser parser) { diff --git a/server/src/main/java/org/elasticsearch/action/search/SearchResponse.java b/server/src/main/java/org/elasticsearch/action/search/SearchResponse.java index 5f42c1543da8c..8a2010a816ec6 100644 --- a/server/src/main/java/org/elasticsearch/action/search/SearchResponse.java +++ b/server/src/main/java/org/elasticsearch/action/search/SearchResponse.java @@ -12,10 +12,12 @@ import org.elasticsearch.TransportVersion; import org.elasticsearch.action.ActionResponse; import org.elasticsearch.common.Strings; +import org.elasticsearch.common.collect.Iterators; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.io.stream.Writeable; -import org.elasticsearch.common.xcontent.StatusToXContentObject; +import org.elasticsearch.common.xcontent.ChunkedToXContentHelper; +import org.elasticsearch.common.xcontent.ChunkedToXContentObject; import org.elasticsearch.core.Nullable; import org.elasticsearch.core.TimeValue; import org.elasticsearch.rest.RestStatus; @@ -29,6 +31,7 @@ import org.elasticsearch.search.profile.SearchProfileShardResult; import org.elasticsearch.search.suggest.Suggest; import org.elasticsearch.xcontent.ParseField; +import org.elasticsearch.xcontent.ToXContent; import org.elasticsearch.xcontent.ToXContentFragment; import org.elasticsearch.xcontent.XContentBuilder; import org.elasticsearch.xcontent.XContentParser; @@ -36,6 +39,7 @@ import java.io.IOException; import java.util.ArrayList; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Objects; @@ -47,7 +51,7 @@ /** * A response of a search request. */ -public class SearchResponse extends ActionResponse implements StatusToXContentObject { +public class SearchResponse extends ActionResponse implements ChunkedToXContentObject { private static final ParseField SCROLL_ID = new ParseField("_scroll_id"); private static final ParseField POINT_IN_TIME_ID = new ParseField("pit_id"); @@ -129,7 +133,6 @@ public SearchResponse( : "SearchResponse can't have both scrollId [" + scrollId + "] and searchContextId [" + pointInTimeId + "]"; } - @Override public RestStatus status() { return RestStatus.status(successfulShards, totalShards, shardFailures); } @@ -264,14 +267,23 @@ public Clusters getClusters() { } @Override - public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { - builder.startObject(); - innerToXContent(builder, params); - builder.endObject(); - return builder; + public Iterator toXContentChunked(ToXContent.Params params) { + return Iterators.concat( + ChunkedToXContentHelper.startObject(), + this.innerToXContentChunked(params), + ChunkedToXContentHelper.endObject() + ); + } + + public Iterator innerToXContentChunked(ToXContent.Params params) { + return Iterators.concat( + ChunkedToXContentHelper.singleChunk(SearchResponse.this::headerToXContent), + Iterators.single(clusters), + internalResponse.toXContentChunked(params) + ); } - public XContentBuilder innerToXContent(XContentBuilder builder, Params params) throws IOException { + public XContentBuilder headerToXContent(XContentBuilder builder, ToXContent.Params params) throws IOException { if (scrollId != null) { builder.field(SCROLL_ID.getPreferredName(), scrollId); } @@ -295,8 +307,6 @@ public XContentBuilder innerToXContent(XContentBuilder builder, Params params) t getFailedShards(), getShardFailures() ); - clusters.toXContent(builder, params); - internalResponse.toXContent(builder, params); return builder; } diff --git a/server/src/main/java/org/elasticsearch/action/search/SearchResponseSections.java b/server/src/main/java/org/elasticsearch/action/search/SearchResponseSections.java index 25a34854cd7df..996653e374435 100644 --- a/server/src/main/java/org/elasticsearch/action/search/SearchResponseSections.java +++ b/server/src/main/java/org/elasticsearch/action/search/SearchResponseSections.java @@ -8,17 +8,19 @@ package org.elasticsearch.action.search; +import org.elasticsearch.common.collect.Iterators; import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.common.xcontent.ChunkedToXContent; import org.elasticsearch.search.SearchHits; import org.elasticsearch.search.aggregations.Aggregations; import org.elasticsearch.search.profile.SearchProfileResults; import org.elasticsearch.search.profile.SearchProfileShardResult; import org.elasticsearch.search.suggest.Suggest; -import org.elasticsearch.xcontent.ToXContentFragment; -import org.elasticsearch.xcontent.XContentBuilder; +import org.elasticsearch.xcontent.ToXContent; import java.io.IOException; import java.util.Collections; +import java.util.Iterator; import java.util.Map; /** @@ -29,7 +31,7 @@ * to parse aggregations into, which are not serializable. This is the common part that can be * shared between core and client. */ -public class SearchResponseSections implements ToXContentFragment { +public class SearchResponseSections implements ChunkedToXContent { protected final SearchHits hits; protected final Aggregations aggregations; @@ -98,18 +100,33 @@ public final Map profile() { } @Override - public final XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { - hits.toXContent(builder, params); - if (aggregations != null) { - aggregations.toXContent(builder, params); - } - if (suggest != null) { - suggest.toXContent(builder, params); - } - if (profileResults != null) { - profileResults.toXContent(builder, params); - } - return builder; + public Iterator toXContentChunked(ToXContent.Params params) { + return Iterators.concat( + Iterators.flatMap(Iterators.single(hits), r -> r.toXContentChunked(params)), + Iterators.single((ToXContent) (b, p) -> { + if (aggregations != null) { + aggregations.toXContent(b, p); + } + return b; + }), + Iterators.single((b, p) -> { + if (suggest != null) { + suggest.toXContent(b, p); + } + return b; + }), + Iterators.single((b, p) -> { + if (profileResults != null) { + profileResults.toXContent(b, p); + } + return b; + }) + ); + } + + @Override + public boolean isFragment() { + return true; } protected void writeTo(StreamOutput out) throws IOException { diff --git a/server/src/main/java/org/elasticsearch/common/xcontent/ChunkedToXContentObject.java b/server/src/main/java/org/elasticsearch/common/xcontent/ChunkedToXContentObject.java index b0fad066cd82d..d90beba1d21ac 100644 --- a/server/src/main/java/org/elasticsearch/common/xcontent/ChunkedToXContentObject.java +++ b/server/src/main/java/org/elasticsearch/common/xcontent/ChunkedToXContentObject.java @@ -7,6 +7,11 @@ */ package org.elasticsearch.common.xcontent; +import org.elasticsearch.xcontent.ToXContent; +import org.elasticsearch.xcontent.ToXContentObject; + +import java.util.Iterator; + /** * Chunked equivalent of {@link org.elasticsearch.xcontent.ToXContentObject} that serializes as a full object. */ @@ -16,4 +21,20 @@ public interface ChunkedToXContentObject extends ChunkedToXContent { default boolean isFragment() { return false; } + + /** + * Wraps the given instance in a {@link ToXContentObject} that will fully serialize the instance when serialized. + * + * @param chunkedToXContent instance to wrap + * @return x-content instance + */ + static ToXContentObject wrapAsToXContentObject(ChunkedToXContentObject chunkedToXContent) { + return (builder, params) -> { + Iterator serialization = chunkedToXContent.toXContentChunked(params); + while (serialization.hasNext()) { + serialization.next().toXContent(builder, params); + } + return builder; + }; + } } diff --git a/server/src/main/java/org/elasticsearch/common/xcontent/XContentHelper.java b/server/src/main/java/org/elasticsearch/common/xcontent/XContentHelper.java index e401cd4b4b211..98d27dbc7cf80 100644 --- a/server/src/main/java/org/elasticsearch/common/xcontent/XContentHelper.java +++ b/server/src/main/java/org/elasticsearch/common/xcontent/XContentHelper.java @@ -496,6 +496,16 @@ public static BytesReference toXContent(ToXContent toXContent, XContentType xCon return toXContent(toXContent, xContentType, ToXContent.EMPTY_PARAMS, humanReadable); } + /** + * Returns the bytes that represent the XContent output of the provided {@link ChunkedToXContent} object, using the provided + * {@link XContentType}. Wraps the output into a new anonymous object according to the value returned + * by the {@link ToXContent#isFragment()} method returns. + */ + public static BytesReference toXContent(ChunkedToXContent toXContent, XContentType xContentType, boolean humanReadable) + throws IOException { + return toXContent(ChunkedToXContent.wrapAsToXContent(toXContent), xContentType, humanReadable); + } + /** * Returns the bytes that represent the XContent output of the provided {@link ToXContent} object, using the provided * {@link XContentType}. Wraps the output into a new anonymous object according to the value returned @@ -516,6 +526,16 @@ public static BytesReference toXContent(ToXContent toXContent, XContentType xCon } } + /** + * Returns the bytes that represent the XContent output of the provided {@link ChunkedToXContent} object, using the provided + * {@link XContentType}. Wraps the output into a new anonymous object according to the value returned + * by the {@link ToXContent#isFragment()} method returns. + */ + public static BytesReference toXContent(ChunkedToXContent toXContent, XContentType xContentType, Params params, boolean humanReadable) + throws IOException { + return toXContent(ChunkedToXContent.wrapAsToXContent(toXContent), xContentType, params, humanReadable); + } + /** * Guesses the content type based on the provided bytes which may be compressed. * diff --git a/server/src/main/java/org/elasticsearch/rest/action/search/RestKnnSearchAction.java b/server/src/main/java/org/elasticsearch/rest/action/search/RestKnnSearchAction.java index b697db7929ecf..681474403eb14 100644 --- a/server/src/main/java/org/elasticsearch/rest/action/search/RestKnnSearchAction.java +++ b/server/src/main/java/org/elasticsearch/rest/action/search/RestKnnSearchAction.java @@ -14,7 +14,7 @@ import org.elasticsearch.rest.BaseRestHandler; import org.elasticsearch.rest.RestRequest; import org.elasticsearch.rest.action.RestCancellableNodeClient; -import org.elasticsearch.rest.action.RestStatusToXContentListener; +import org.elasticsearch.rest.action.RestChunkedToXContentListener; import org.elasticsearch.search.vectors.KnnSearchRequestParser; import java.io.IOException; @@ -55,6 +55,6 @@ protected RestChannelConsumer prepareRequest(RestRequest restRequest, NodeClient SearchRequestBuilder searchRequestBuilder = cancellableNodeClient.prepareSearch(); parser.toSearchRequest(searchRequestBuilder); - return channel -> searchRequestBuilder.execute(new RestStatusToXContentListener<>(channel)); + return channel -> searchRequestBuilder.execute(new RestChunkedToXContentListener<>(channel)); } } diff --git a/server/src/main/java/org/elasticsearch/rest/action/search/RestMultiSearchAction.java b/server/src/main/java/org/elasticsearch/rest/action/search/RestMultiSearchAction.java index bca9a80fe529b..c9bcaf6b5ff4d 100644 --- a/server/src/main/java/org/elasticsearch/rest/action/search/RestMultiSearchAction.java +++ b/server/src/main/java/org/elasticsearch/rest/action/search/RestMultiSearchAction.java @@ -26,7 +26,7 @@ import org.elasticsearch.rest.Scope; import org.elasticsearch.rest.ServerlessScope; import org.elasticsearch.rest.action.RestCancellableNodeClient; -import org.elasticsearch.rest.action.RestToXContentListener; +import org.elasticsearch.rest.action.RestChunkedToXContentListener; import org.elasticsearch.search.builder.SearchSourceBuilder; import org.elasticsearch.usage.SearchUsageHolder; import org.elasticsearch.xcontent.XContent; @@ -82,7 +82,7 @@ public RestChannelConsumer prepareRequest(final RestRequest request, final NodeC ); return channel -> { final RestCancellableNodeClient cancellableClient = new RestCancellableNodeClient(client, request.getHttpChannel()); - cancellableClient.execute(MultiSearchAction.INSTANCE, multiSearchRequest, new RestToXContentListener<>(channel)); + cancellableClient.execute(MultiSearchAction.INSTANCE, multiSearchRequest, new RestChunkedToXContentListener<>(channel)); }; } diff --git a/server/src/main/java/org/elasticsearch/rest/action/search/RestSearchAction.java b/server/src/main/java/org/elasticsearch/rest/action/search/RestSearchAction.java index 29627d5233ada..9a5aef4996209 100644 --- a/server/src/main/java/org/elasticsearch/rest/action/search/RestSearchAction.java +++ b/server/src/main/java/org/elasticsearch/rest/action/search/RestSearchAction.java @@ -29,7 +29,7 @@ import org.elasticsearch.rest.ServerlessScope; import org.elasticsearch.rest.action.RestActions; import org.elasticsearch.rest.action.RestCancellableNodeClient; -import org.elasticsearch.rest.action.RestStatusToXContentListener; +import org.elasticsearch.rest.action.RestChunkedToXContentListener; import org.elasticsearch.search.Scroll; import org.elasticsearch.search.SearchService; import org.elasticsearch.search.builder.SearchSourceBuilder; @@ -121,7 +121,7 @@ public RestChannelConsumer prepareRequest(final RestRequest request, final NodeC return channel -> { RestCancellableNodeClient cancelClient = new RestCancellableNodeClient(client, request.getHttpChannel()); - cancelClient.execute(SearchAction.INSTANCE, searchRequest, new RestStatusToXContentListener<>(channel)); + cancelClient.execute(SearchAction.INSTANCE, searchRequest, new RestChunkedToXContentListener<>(channel)); }; } diff --git a/server/src/main/java/org/elasticsearch/rest/action/search/RestSearchScrollAction.java b/server/src/main/java/org/elasticsearch/rest/action/search/RestSearchScrollAction.java index f69d80a7a5538..de2101f94de2a 100644 --- a/server/src/main/java/org/elasticsearch/rest/action/search/RestSearchScrollAction.java +++ b/server/src/main/java/org/elasticsearch/rest/action/search/RestSearchScrollAction.java @@ -14,7 +14,7 @@ import org.elasticsearch.rest.RestRequest; import org.elasticsearch.rest.Scope; import org.elasticsearch.rest.ServerlessScope; -import org.elasticsearch.rest.action.RestStatusToXContentListener; +import org.elasticsearch.rest.action.RestChunkedToXContentListener; import org.elasticsearch.search.Scroll; import org.elasticsearch.xcontent.XContentParseException; @@ -66,7 +66,7 @@ public RestChannelConsumer prepareRequest(final RestRequest request, final NodeC } } }); - return channel -> client.searchScroll(searchScrollRequest, new RestStatusToXContentListener<>(channel)); + return channel -> client.searchScroll(searchScrollRequest, new RestChunkedToXContentListener<>(channel)); } @Override diff --git a/server/src/main/java/org/elasticsearch/search/SearchHit.java b/server/src/main/java/org/elasticsearch/search/SearchHit.java index d3f3c9c88eb20..35e8813351847 100644 --- a/server/src/main/java/org/elasticsearch/search/SearchHit.java +++ b/server/src/main/java/org/elasticsearch/search/SearchHit.java @@ -21,6 +21,7 @@ import org.elasticsearch.common.io.stream.Writeable; import org.elasticsearch.common.text.Text; import org.elasticsearch.common.util.Maps; +import org.elasticsearch.common.xcontent.ChunkedToXContent; import org.elasticsearch.common.xcontent.XContentHelper; import org.elasticsearch.common.xcontent.support.XContentMapValues; import org.elasticsearch.core.Nullable; @@ -782,7 +783,7 @@ public XContentBuilder toInnerXContent(XContentBuilder builder, Params params) t builder.startObject(Fields.INNER_HITS); for (Map.Entry entry : innerHits.entrySet()) { builder.startObject(entry.getKey()); - entry.getValue().toXContent(builder, params); + ChunkedToXContent.wrapAsToXContent(entry.getValue()).toXContent(builder, params); builder.endObject(); } builder.endObject(); diff --git a/server/src/main/java/org/elasticsearch/search/SearchHits.java b/server/src/main/java/org/elasticsearch/search/SearchHits.java index 89113a0f10365..13fc9214f07bf 100644 --- a/server/src/main/java/org/elasticsearch/search/SearchHits.java +++ b/server/src/main/java/org/elasticsearch/search/SearchHits.java @@ -16,10 +16,11 @@ import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.io.stream.Writeable; import org.elasticsearch.common.lucene.Lucene; +import org.elasticsearch.common.xcontent.ChunkedToXContent; +import org.elasticsearch.common.xcontent.ChunkedToXContentHelper; import org.elasticsearch.core.Nullable; import org.elasticsearch.rest.action.search.RestSearchAction; -import org.elasticsearch.xcontent.ToXContentFragment; -import org.elasticsearch.xcontent.XContentBuilder; +import org.elasticsearch.xcontent.ToXContent; import org.elasticsearch.xcontent.XContentParser; import java.io.IOException; @@ -31,7 +32,7 @@ import static org.elasticsearch.common.xcontent.XContentParserUtils.ensureExpectedToken; -public final class SearchHits implements Writeable, ToXContentFragment, Iterable { +public final class SearchHits implements Writeable, ChunkedToXContent, Iterable { public static final SearchHit[] EMPTY = new SearchHit[0]; public static final SearchHits EMPTY_WITH_TOTAL_HITS = new SearchHits(EMPTY, new TotalHits(0, Relation.EQUAL_TO), 0); @@ -170,31 +171,27 @@ public static final class Fields { } @Override - public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { - builder.startObject(Fields.HITS); - boolean totalHitAsInt = params.paramAsBoolean(RestSearchAction.TOTAL_HITS_AS_INT_PARAM, false); - if (totalHitAsInt) { - long total = totalHits == null ? -1 : totalHits.value; - builder.field(Fields.TOTAL, total); - } else if (totalHits != null) { - builder.startObject(Fields.TOTAL); - builder.field("value", totalHits.value); - builder.field("relation", totalHits.relation == Relation.EQUAL_TO ? "eq" : "gte"); - builder.endObject(); - } - if (Float.isNaN(maxScore)) { - builder.nullField(Fields.MAX_SCORE); - } else { - builder.field(Fields.MAX_SCORE, maxScore); - } - builder.field(Fields.HITS); - builder.startArray(); - for (SearchHit hit : hits) { - hit.toXContent(builder, params); - } - builder.endArray(); - builder.endObject(); - return builder; + public Iterator toXContentChunked(ToXContent.Params params) { + return Iterators.concat(Iterators.single((b, p) -> b.startObject(Fields.HITS)), Iterators.single((b, p) -> { + boolean totalHitAsInt = params.paramAsBoolean(RestSearchAction.TOTAL_HITS_AS_INT_PARAM, false); + if (totalHitAsInt) { + long total = totalHits == null ? -1 : totalHits.value; + b.field(Fields.TOTAL, total); + } else if (totalHits != null) { + b.startObject(Fields.TOTAL); + b.field("value", totalHits.value); + b.field("relation", totalHits.relation == Relation.EQUAL_TO ? "eq" : "gte"); + b.endObject(); + } + return b; + }), Iterators.single((b, p) -> { + if (Float.isNaN(maxScore)) { + b.nullField(Fields.MAX_SCORE); + } else { + b.field(Fields.MAX_SCORE, maxScore); + } + return b; + }), ChunkedToXContentHelper.array(Fields.HITS, Iterators.forArray(hits)), ChunkedToXContentHelper.endObject()); } public static SearchHits fromXContent(XContentParser parser) throws IOException { diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/metrics/InternalTopHits.java b/server/src/main/java/org/elasticsearch/search/aggregations/metrics/InternalTopHits.java index b87df7c87ee27..03c35eda056ee 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/metrics/InternalTopHits.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/metrics/InternalTopHits.java @@ -17,6 +17,7 @@ import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.lucene.Lucene; import org.elasticsearch.common.lucene.search.TopDocsAndMaxScore; +import org.elasticsearch.common.xcontent.ChunkedToXContent; import org.elasticsearch.search.SearchHit; import org.elasticsearch.search.SearchHits; import org.elasticsearch.search.aggregations.AggregationReduceContext; @@ -33,10 +34,10 @@ * Results of the {@link TopHitsAggregator}. */ public class InternalTopHits extends InternalAggregation implements TopHits { - private int from; - private int size; - private TopDocsAndMaxScore topDocs; - private SearchHits searchHits; + private final int from; + private final int size; + private final TopDocsAndMaxScore topDocs; + private final SearchHits searchHits; public InternalTopHits( String name, @@ -191,7 +192,7 @@ public Object getProperty(List path) { @Override public XContentBuilder doXContentBody(XContentBuilder builder, Params params) throws IOException { - searchHits.toXContent(builder, params); + ChunkedToXContent.wrapAsToXContent(searchHits).toXContent(builder, params); return builder; } diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/metrics/ParsedTopHits.java b/server/src/main/java/org/elasticsearch/search/aggregations/metrics/ParsedTopHits.java index 0ab953cdacd2c..4eece5b577bf9 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/metrics/ParsedTopHits.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/metrics/ParsedTopHits.java @@ -8,6 +8,7 @@ package org.elasticsearch.search.aggregations.metrics; +import org.elasticsearch.common.xcontent.ChunkedToXContent; import org.elasticsearch.search.SearchHits; import org.elasticsearch.search.aggregations.ParsedAggregation; import org.elasticsearch.xcontent.ObjectParser; @@ -33,7 +34,7 @@ public SearchHits getHits() { @Override protected XContentBuilder doXContentBody(XContentBuilder builder, Params params) throws IOException { - return searchHits.toXContent(builder, params); + return ChunkedToXContent.wrapAsToXContent(searchHits).toXContent(builder, params); } private static final ObjectParser PARSER = new ObjectParser<>( diff --git a/server/src/main/java/org/elasticsearch/search/internal/InternalSearchResponse.java b/server/src/main/java/org/elasticsearch/search/internal/InternalSearchResponse.java index 8ec23c6b82ddb..392f60ba36cd0 100644 --- a/server/src/main/java/org/elasticsearch/search/internal/InternalSearchResponse.java +++ b/server/src/main/java/org/elasticsearch/search/internal/InternalSearchResponse.java @@ -16,14 +16,13 @@ import org.elasticsearch.search.aggregations.InternalAggregations; import org.elasticsearch.search.profile.SearchProfileResults; import org.elasticsearch.search.suggest.Suggest; -import org.elasticsearch.xcontent.ToXContentFragment; import java.io.IOException; /** * {@link SearchResponseSections} subclass that can be serialized over the wire. */ -public class InternalSearchResponse extends SearchResponseSections implements Writeable, ToXContentFragment { +public class InternalSearchResponse extends SearchResponseSections implements Writeable { public static final InternalSearchResponse EMPTY_WITH_TOTAL_HITS = new InternalSearchResponse( SearchHits.EMPTY_WITH_TOTAL_HITS, null, diff --git a/server/src/test/java/org/elasticsearch/action/search/MultiSearchResponseTests.java b/server/src/test/java/org/elasticsearch/action/search/MultiSearchResponseTests.java index e17b6766bb5e7..924e5d753b4da 100644 --- a/server/src/test/java/org/elasticsearch/action/search/MultiSearchResponseTests.java +++ b/server/src/test/java/org/elasticsearch/action/search/MultiSearchResponseTests.java @@ -8,24 +8,26 @@ package org.elasticsearch.action.search; import org.elasticsearch.ElasticsearchException; -import org.elasticsearch.common.Strings; import org.elasticsearch.search.internal.InternalSearchResponse; -import org.elasticsearch.test.AbstractXContentTestCase; +import org.elasticsearch.test.ESTestCase; import org.elasticsearch.xcontent.ToXContent; import org.elasticsearch.xcontent.XContentParser; import java.io.IOException; import java.util.function.Predicate; -import java.util.function.Supplier; +import static org.elasticsearch.test.AbstractXContentTestCase.chunkedXContentTester; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.nullValue; -public class MultiSearchResponseTests extends AbstractXContentTestCase { +public class MultiSearchResponseTests extends ESTestCase { - @Override - protected MultiSearchResponse createTestInstance() { + // We can not subclass AbstractSerializingTestCase because it + // can only be used for instances with equals and hashCode + // MultiSearchResponse does not override equals and hashCode. + + private MultiSearchResponse createTestInstance() { int numItems = randomIntBetween(0, 128); MultiSearchResponse.Item[] items = new MultiSearchResponse.Item[numItems]; for (int i = 0; i < numItems; i++) { @@ -84,13 +86,11 @@ private static MultiSearchResponse createTestInstanceWithFailures() { return new MultiSearchResponse(items, randomNonNegativeLong()); } - @Override - protected MultiSearchResponse doParseInstance(XContentParser parser) throws IOException { + private MultiSearchResponse doParseInstance(XContentParser parser) throws IOException { return MultiSearchResponse.fromXContext(parser); } - @Override - protected void assertEqualInstances(MultiSearchResponse expected, MultiSearchResponse actual) { + private void assertEqualInstances(MultiSearchResponse expected, MultiSearchResponse actual) { assertThat(actual.getTook(), equalTo(expected.getTook())); assertThat(actual.getResponses().length, equalTo(expected.getResponses().length)); for (int i = 0; i < expected.getResponses().length; i++) { @@ -106,8 +106,7 @@ protected void assertEqualInstances(MultiSearchResponse expected, MultiSearchRes } } - @Override - protected boolean supportsUnknownFields() { + private boolean supportsUnknownFields() { return true; } @@ -115,30 +114,31 @@ protected Predicate getRandomFieldsExcludeFilterWhenResultHasErrors() { return field -> field.startsWith("responses"); } + public final void testFromXContent() throws IOException { + chunkedXContentTester(this::createParser, t -> createTestInstance(), ToXContent.EMPTY_PARAMS, this::doParseInstance) + .numberOfTestRuns(20) + .supportsUnknownFields(supportsUnknownFields()) + .assertEqualsConsumer(this::assertEqualInstances) + .test(); + } + /** * Test parsing {@link MultiSearchResponse} with inner failures as they don't support asserting on xcontent equivalence, given that - * exceptions are not parsed back as the same original class. We run the usual {@link AbstractXContentTestCase#testFromXContent()} + * exceptions are not parsed back as the same original class. We run the usual + * {@link org.elasticsearch.test.AbstractSerializationTestCase#testFromXContent()} * without failures, and this other test with failures where we disable asserting on xcontent equivalence at the end. */ public void testFromXContentWithFailures() throws IOException { - Supplier instanceSupplier = MultiSearchResponseTests::createTestInstanceWithFailures; - // with random fields insertion in the inner exceptions, some random stuff may be parsed back as metadata, - // but that does not bother our assertions, as we only want to test that we don't break. - boolean supportsUnknownFields = true; - // exceptions are not of the same type whenever parsed back - boolean assertToXContentEquivalence = false; - AbstractXContentTestCase.testFromXContent( - NUMBER_OF_TEST_RUNS, - instanceSupplier, - supportsUnknownFields, - Strings.EMPTY_ARRAY, - getRandomFieldsExcludeFilterWhenResultHasErrors(), - this::createParser, - this::doParseInstance, - this::assertEqualInstances, - assertToXContentEquivalence, - ToXContent.EMPTY_PARAMS - ); + chunkedXContentTester(this::createParser, t -> createTestInstanceWithFailures(), ToXContent.EMPTY_PARAMS, this::doParseInstance) + .numberOfTestRuns(20) + .randomFieldsExcludeFilter(getRandomFieldsExcludeFilterWhenResultHasErrors()) + // with random fields insertion in the inner exceptions, some random stuff may be parsed back as metadata, + // but that does not bother our assertions, as we only want to test that we don't break. + .supportsUnknownFields(true) + // exceptions are not of the same type whenever parsed back + .assertToXContentEquivalence(false) + .assertEqualsConsumer(this::assertEqualInstances) + .test(); } } diff --git a/server/src/test/java/org/elasticsearch/action/search/SearchResponseTests.java b/server/src/test/java/org/elasticsearch/action/search/SearchResponseTests.java index fa38d6ecd430f..a02d40bad3f51 100644 --- a/server/src/test/java/org/elasticsearch/action/search/SearchResponseTests.java +++ b/server/src/test/java/org/elasticsearch/action/search/SearchResponseTests.java @@ -15,6 +15,7 @@ import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.xcontent.ChunkedToXContent; import org.elasticsearch.common.xcontent.XContentHelper; import org.elasticsearch.rest.action.search.RestSearchAction; import org.elasticsearch.search.SearchHit; @@ -159,7 +160,12 @@ private void doFromXContentTestWithRandomFields(SearchResponse response, boolean XContentType xcontentType = randomFrom(XContentType.values()); boolean humanReadable = randomBoolean(); final ToXContent.Params params = new ToXContent.MapParams(singletonMap(RestSearchAction.TYPED_KEYS_PARAM, "true")); - BytesReference originalBytes = toShuffledXContent(response, xcontentType, params, humanReadable); + BytesReference originalBytes = toShuffledXContent( + ChunkedToXContent.wrapAsToXContent(response), + xcontentType, + params, + humanReadable + ); BytesReference mutated; if (addRandomFields) { mutated = insertRandomFields(xcontentType, originalBytes, null, random()); @@ -189,7 +195,12 @@ public void testFromXContentWithFailures() throws IOException { SearchResponse response = createTestItem(failures); XContentType xcontentType = randomFrom(XContentType.values()); final ToXContent.Params params = new ToXContent.MapParams(singletonMap(RestSearchAction.TYPED_KEYS_PARAM, "true")); - BytesReference originalBytes = toShuffledXContent(response, xcontentType, params, randomBoolean()); + BytesReference originalBytes = toShuffledXContent( + ChunkedToXContent.wrapAsToXContent(response), + xcontentType, + params, + randomBoolean() + ); try (XContentParser parser = createParser(xcontentType.xContent(), originalBytes)) { SearchResponse parsed = SearchResponse.fromXContent(parser); for (int i = 0; i < parsed.getShardFailures().length; i++) { diff --git a/server/src/test/java/org/elasticsearch/search/SearchHitsTests.java b/server/src/test/java/org/elasticsearch/search/SearchHitsTests.java index 528223fc40075..1e720064dab56 100644 --- a/server/src/test/java/org/elasticsearch/search/SearchHitsTests.java +++ b/server/src/test/java/org/elasticsearch/search/SearchHitsTests.java @@ -15,11 +15,12 @@ import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.io.stream.Writeable; import org.elasticsearch.common.lucene.LuceneTests; +import org.elasticsearch.common.xcontent.ChunkedToXContent; import org.elasticsearch.common.xcontent.LoggingDeprecationHandler; import org.elasticsearch.common.xcontent.XContentHelper; import org.elasticsearch.index.Index; import org.elasticsearch.index.shard.ShardId; -import org.elasticsearch.test.AbstractXContentSerializingTestCase; +import org.elasticsearch.test.AbstractChunkedSerializingTestCase; import org.elasticsearch.xcontent.ToXContent; import org.elasticsearch.xcontent.XContentBuilder; import org.elasticsearch.xcontent.XContentParser; @@ -29,7 +30,7 @@ import java.io.IOException; import java.util.function.Predicate; -public class SearchHitsTests extends AbstractXContentSerializingTestCase { +public class SearchHitsTests extends AbstractChunkedSerializingTestCase { public static SearchHits createTestItem(boolean withOptionalInnerHits, boolean withShardTarget) { return createTestItem(randomFrom(XContentType.values()).canonical(), withOptionalInnerHits, withShardTarget); @@ -233,7 +234,7 @@ public void testToXContent() throws IOException { SearchHits searchHits = new SearchHits(hits, new TotalHits(totalHits, TotalHits.Relation.EQUAL_TO), maxScore); XContentBuilder builder = JsonXContent.contentBuilder(); builder.startObject(); - searchHits.toXContent(builder, ToXContent.EMPTY_PARAMS); + ChunkedToXContent.wrapAsToXContent(searchHits).toXContent(builder, ToXContent.EMPTY_PARAMS); builder.endObject(); assertEquals(XContentHelper.stripWhitespace(""" { @@ -270,7 +271,12 @@ public void testFromXContentWithShards() throws IOException { float maxScore = 1.5f; SearchHits searchHits = new SearchHits(hits, new TotalHits(totalHits, TotalHits.Relation.EQUAL_TO), maxScore); XContentType xContentType = randomFrom(XContentType.values()).canonical(); - BytesReference bytes = toShuffledXContent(searchHits, xContentType, ToXContent.EMPTY_PARAMS, false); + BytesReference bytes = toShuffledXContent( + ChunkedToXContent.wrapAsToXContent(searchHits), + xContentType, + ToXContent.EMPTY_PARAMS, + false + ); try ( XContentParser parser = xContentType.xContent() .createParser(xContentRegistry(), LoggingDeprecationHandler.INSTANCE, bytes.streamInput()) diff --git a/server/src/test/java/org/elasticsearch/search/aggregations/metrics/InternalTopHitsTests.java b/server/src/test/java/org/elasticsearch/search/aggregations/metrics/InternalTopHitsTests.java index 16c36697cfc3f..945cba465b4ed 100644 --- a/server/src/test/java/org/elasticsearch/search/aggregations/metrics/InternalTopHitsTests.java +++ b/server/src/test/java/org/elasticsearch/search/aggregations/metrics/InternalTopHitsTests.java @@ -21,6 +21,7 @@ import org.elasticsearch.common.document.DocumentField; import org.elasticsearch.common.lucene.search.TopDocsAndMaxScore; import org.elasticsearch.common.util.Maps; +import org.elasticsearch.common.xcontent.ChunkedToXContent; import org.elasticsearch.common.xcontent.XContentHelper; import org.elasticsearch.core.Tuple; import org.elasticsearch.search.SearchHit; @@ -288,7 +289,7 @@ protected void assertSampled(InternalTopHits sampled, InternalTopHits reduced, S * Assert that two objects are equals, calling {@link ToXContent#toXContent(XContentBuilder, ToXContent.Params)} to print out their * differences if they aren't equal. */ - private static void assertEqualsWithErrorMessageFromXContent(T expected, T actual) { + private static void assertEqualsWithErrorMessageFromXContent(T expected, T actual) { if (Objects.equals(expected, actual)) { return; } @@ -300,10 +301,10 @@ private static void assertEqualsWithErrorMessageFromXCont } try (XContentBuilder actualJson = JsonXContent.contentBuilder(); XContentBuilder expectedJson = JsonXContent.contentBuilder()) { actualJson.startObject(); - actual.toXContent(actualJson, ToXContent.EMPTY_PARAMS); + ChunkedToXContent.wrapAsToXContent(actual).toXContent(actualJson, ToXContent.EMPTY_PARAMS); actualJson.endObject(); expectedJson.startObject(); - expected.toXContent(expectedJson, ToXContent.EMPTY_PARAMS); + ChunkedToXContent.wrapAsToXContent(expected).toXContent(expectedJson, ToXContent.EMPTY_PARAMS); expectedJson.endObject(); NotEqualMessageBuilder message = new NotEqualMessageBuilder(); message.compareMaps( diff --git a/test/framework/src/main/java/org/elasticsearch/search/aggregations/metrics/AbstractGeoTestCase.java b/test/framework/src/main/java/org/elasticsearch/search/aggregations/metrics/AbstractGeoTestCase.java index fb4a687717a12..1af8eb8dab82b 100644 --- a/test/framework/src/main/java/org/elasticsearch/search/aggregations/metrics/AbstractGeoTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/search/aggregations/metrics/AbstractGeoTestCase.java @@ -14,6 +14,7 @@ import org.elasticsearch.common.document.DocumentField; import org.elasticsearch.common.geo.SpatialPoint; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.xcontent.ChunkedToXContent; import org.elasticsearch.geometry.utils.Geohash; import org.elasticsearch.search.SearchHit; import org.elasticsearch.search.sort.SortBuilders; @@ -236,7 +237,7 @@ public void setupSuiteScopeCluster() throws Exception { assertSearchResponse(response); long totalHits = response.getHits().getTotalHits().value; XContentBuilder builder = XContentFactory.jsonBuilder(); - response.toXContent(builder, ToXContent.EMPTY_PARAMS); + ChunkedToXContent.wrapAsToXContent(response).toXContent(builder, ToXContent.EMPTY_PARAMS); logger.info("Full high_card_idx Response Content:\n{ {} }", Strings.toString(builder)); for (int i = 0; i < totalHits; i++) { SearchHit searchHit = response.getHits().getAt(i); diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/search/action/AsyncSearchResponse.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/search/action/AsyncSearchResponse.java index b05d30289b528..63b12d51f280c 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/search/action/AsyncSearchResponse.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/search/action/AsyncSearchResponse.java @@ -12,6 +12,7 @@ import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.common.xcontent.ChunkedToXContent; import org.elasticsearch.common.xcontent.StatusToXContentObject; import org.elasticsearch.core.Nullable; import org.elasticsearch.rest.RestStatus; @@ -194,7 +195,7 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws if (searchResponse != null) { builder.field("response"); - searchResponse.toXContent(builder, params); + ChunkedToXContent.wrapAsToXContent(searchResponse).toXContent(builder, params); } if (error != null) { builder.startObject("error"); diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/watcher/watch/Payload.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/watcher/watch/Payload.java index 46803619f562e..98dc66e093114 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/watcher/watch/Payload.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/watcher/watch/Payload.java @@ -8,6 +8,7 @@ import org.elasticsearch.common.collect.MapBuilder; import org.elasticsearch.common.util.CollectionUtils; +import org.elasticsearch.common.xcontent.ChunkedToXContentObject; import org.elasticsearch.xcontent.ToXContentObject; import org.elasticsearch.xcontent.XContentBuilder; @@ -79,5 +80,9 @@ class XContent extends Simple { public XContent(ToXContentObject response, Params params) throws IOException { super(responseToData(response, params)); } + + public XContent(ChunkedToXContentObject response, Params params) throws IOException { + this(ChunkedToXContentObject.wrapAsToXContentObject(response), params); + } } } diff --git a/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/search/action/RestQuerySearchApplicationAction.java b/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/search/action/RestQuerySearchApplicationAction.java index c9a67d2d858c6..fd59521c581be 100644 --- a/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/search/action/RestQuerySearchApplicationAction.java +++ b/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/search/action/RestQuerySearchApplicationAction.java @@ -12,7 +12,7 @@ import org.elasticsearch.rest.RestRequest; import org.elasticsearch.rest.Scope; import org.elasticsearch.rest.ServerlessScope; -import org.elasticsearch.rest.action.RestToXContentListener; +import org.elasticsearch.rest.action.RestChunkedToXContentListener; import org.elasticsearch.xpack.application.EnterpriseSearch; import java.io.IOException; @@ -46,6 +46,6 @@ protected RestChannelConsumer prepareRequest(RestRequest restRequest, NodeClient request = new SearchApplicationSearchRequest(searchAppName); } final SearchApplicationSearchRequest finalRequest = request; - return channel -> client.execute(QuerySearchApplicationAction.INSTANCE, finalRequest, new RestToXContentListener<>(channel)); + return channel -> client.execute(QuerySearchApplicationAction.INSTANCE, finalRequest, new RestChunkedToXContentListener<>(channel)); } } diff --git a/x-pack/plugin/fleet/src/main/java/org/elasticsearch/xpack/fleet/rest/RestFleetMultiSearchAction.java b/x-pack/plugin/fleet/src/main/java/org/elasticsearch/xpack/fleet/rest/RestFleetMultiSearchAction.java index e78f5792f0a35..308af9b89e074 100644 --- a/x-pack/plugin/fleet/src/main/java/org/elasticsearch/xpack/fleet/rest/RestFleetMultiSearchAction.java +++ b/x-pack/plugin/fleet/src/main/java/org/elasticsearch/xpack/fleet/rest/RestFleetMultiSearchAction.java @@ -18,7 +18,7 @@ import org.elasticsearch.rest.Scope; import org.elasticsearch.rest.ServerlessScope; import org.elasticsearch.rest.action.RestCancellableNodeClient; -import org.elasticsearch.rest.action.RestToXContentListener; +import org.elasticsearch.rest.action.RestChunkedToXContentListener; import org.elasticsearch.rest.action.search.RestMultiSearchAction; import org.elasticsearch.rest.action.search.RestSearchAction; import org.elasticsearch.usage.SearchUsageHolder; @@ -112,7 +112,7 @@ protected RestChannelConsumer prepareRequest(RestRequest request, NodeClient cli return channel -> { final RestCancellableNodeClient cancellableClient = new RestCancellableNodeClient(client, request.getHttpChannel()); - cancellableClient.execute(MultiSearchAction.INSTANCE, multiSearchRequest, new RestToXContentListener<>(channel)); + cancellableClient.execute(MultiSearchAction.INSTANCE, multiSearchRequest, new RestChunkedToXContentListener<>(channel)); }; } diff --git a/x-pack/plugin/fleet/src/main/java/org/elasticsearch/xpack/fleet/rest/RestFleetSearchAction.java b/x-pack/plugin/fleet/src/main/java/org/elasticsearch/xpack/fleet/rest/RestFleetSearchAction.java index 7df6d70f918e8..178db0229ca58 100644 --- a/x-pack/plugin/fleet/src/main/java/org/elasticsearch/xpack/fleet/rest/RestFleetSearchAction.java +++ b/x-pack/plugin/fleet/src/main/java/org/elasticsearch/xpack/fleet/rest/RestFleetSearchAction.java @@ -18,7 +18,7 @@ import org.elasticsearch.rest.Scope; import org.elasticsearch.rest.ServerlessScope; import org.elasticsearch.rest.action.RestCancellableNodeClient; -import org.elasticsearch.rest.action.RestStatusToXContentListener; +import org.elasticsearch.rest.action.RestChunkedToXContentListener; import org.elasticsearch.rest.action.search.RestSearchAction; import org.elasticsearch.usage.SearchUsageHolder; @@ -96,7 +96,7 @@ protected RestChannelConsumer prepareRequest(RestRequest request, NodeClient cli return channel -> { RestCancellableNodeClient cancelClient = new RestCancellableNodeClient(client, request.getHttpChannel()); - cancelClient.execute(SearchAction.INSTANCE, searchRequest, new RestStatusToXContentListener<>(channel)); + cancelClient.execute(SearchAction.INSTANCE, searchRequest, new RestChunkedToXContentListener<>(channel)); }; } diff --git a/x-pack/plugin/rollup/src/main/java/org/elasticsearch/xpack/rollup/rest/RestRollupSearchAction.java b/x-pack/plugin/rollup/src/main/java/org/elasticsearch/xpack/rollup/rest/RestRollupSearchAction.java index e888776cd1b4a..693daeaee030a 100644 --- a/x-pack/plugin/rollup/src/main/java/org/elasticsearch/xpack/rollup/rest/RestRollupSearchAction.java +++ b/x-pack/plugin/rollup/src/main/java/org/elasticsearch/xpack/rollup/rest/RestRollupSearchAction.java @@ -10,7 +10,7 @@ import org.elasticsearch.client.internal.node.NodeClient; import org.elasticsearch.rest.BaseRestHandler; import org.elasticsearch.rest.RestRequest; -import org.elasticsearch.rest.action.RestToXContentListener; +import org.elasticsearch.rest.action.RestChunkedToXContentListener; import org.elasticsearch.rest.action.search.RestSearchAction; import org.elasticsearch.xpack.core.rollup.action.RollupSearchAction; @@ -48,7 +48,7 @@ protected RestChannelConsumer prepareRequest(RestRequest restRequest, NodeClient ) ); RestSearchAction.validateSearchRequest(restRequest, searchRequest); - return channel -> client.execute(RollupSearchAction.INSTANCE, searchRequest, new RestToXContentListener<>(channel)); + return channel -> client.execute(RollupSearchAction.INSTANCE, searchRequest, new RestChunkedToXContentListener<>(channel)); } @Override diff --git a/x-pack/plugin/vector-tile/src/main/java/org/elasticsearch/xpack/vectortile/rest/RestVectorTileAction.java b/x-pack/plugin/vector-tile/src/main/java/org/elasticsearch/xpack/vectortile/rest/RestVectorTileAction.java index a7db267153fc1..9daa13ac72d65 100644 --- a/x-pack/plugin/vector-tile/src/main/java/org/elasticsearch/xpack/vectortile/rest/RestVectorTileAction.java +++ b/x-pack/plugin/vector-tile/src/main/java/org/elasticsearch/xpack/vectortile/rest/RestVectorTileAction.java @@ -19,6 +19,7 @@ import org.elasticsearch.common.geo.GeoUtils; import org.elasticsearch.common.io.Streams; import org.elasticsearch.common.io.stream.BytesStream; +import org.elasticsearch.common.xcontent.ChunkedToXContent; import org.elasticsearch.geometry.Rectangle; import org.elasticsearch.index.query.BoolQueryBuilder; import org.elasticsearch.index.query.QueryBuilder; @@ -414,7 +415,7 @@ private static VectorTile.Tile.Layer.Builder buildMetaLayer( final Rectangle tile = request.getBoundingBox(); featureBuilder.mergeFrom(featureFactory.box(tile.getMinLon(), tile.getMaxLon(), tile.getMinLat(), tile.getMaxLat())); } - VectorTileUtils.addToXContentToFeature(featureBuilder, layerProps, response); + VectorTileUtils.addToXContentToFeature(featureBuilder, layerProps, ChunkedToXContent.wrapAsToXContent(response)); metaLayerBuilder.addFeatures(featureBuilder); VectorTileUtils.addPropertiesToLayer(metaLayerBuilder, layerProps); return metaLayerBuilder;