/* * SPDX-License-Identifier: Apache-2.0 * * The OpenSearch Contributors require contributions made to * this file be licensed under the Apache-2.0 license or a * compatible open source license. */ package org.opensearch.action.search; import org.opensearch.action.ActionResponse; import org.opensearch.core.ParseField; import org.opensearch.core.common.io.stream.StreamInput; import org.opensearch.core.common.io.stream.StreamOutput; import org.opensearch.common.xcontent.StatusToXContentObject; import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.core.xcontent.XContentParser; import org.opensearch.core.rest.RestStatus; import org.opensearch.rest.action.RestActions; import java.io.IOException; import java.util.ArrayList; import java.util.List; import static org.opensearch.core.xcontent.XContentParserUtils.ensureExpectedToken; /** * Create point in time response with point in time id and shard success / failures */ public class CreatePitResponse extends ActionResponse implements StatusToXContentObject { private static final ParseField ID = new ParseField("pit_id"); private static final ParseField CREATION_TIME = new ParseField("creation_time"); // point in time id private final String id; private final int totalShards; private final int successfulShards; private final int failedShards; private final int skippedShards; private final ShardSearchFailure[] shardFailures; private final long creationTime; public CreatePitResponse(StreamInput in) throws IOException { super(in); id = in.readString(); totalShards = in.readVInt(); successfulShards = in.readVInt(); failedShards = in.readVInt(); skippedShards = in.readVInt(); creationTime = in.readLong(); int size = in.readVInt(); if (size == 0) { shardFailures = ShardSearchFailure.EMPTY_ARRAY; } else { shardFailures = new ShardSearchFailure[size]; for (int i = 0; i < shardFailures.length; i++) { shardFailures[i] = ShardSearchFailure.readShardSearchFailure(in); } } } public CreatePitResponse( String id, long creationTime, int totalShards, int successfulShards, int skippedShards, int failedShards, ShardSearchFailure[] shardFailures ) { this.id = id; this.creationTime = creationTime; this.totalShards = totalShards; this.successfulShards = successfulShards; this.skippedShards = skippedShards; this.failedShards = failedShards; this.shardFailures = shardFailures; } @Override public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { builder.startObject(); builder.field(ID.getPreferredName(), id); RestActions.buildBroadcastShardsHeader( builder, params, getTotalShards(), getSuccessfulShards(), getSkippedShards(), getFailedShards(), getShardFailures() ); builder.field(CREATION_TIME.getPreferredName(), creationTime); builder.endObject(); return builder; } /** * Parse the create PIT response body into a new {@link CreatePitResponse} object */ public static CreatePitResponse fromXContent(XContentParser parser) throws IOException { ensureExpectedToken(XContentParser.Token.START_OBJECT, parser.nextToken(), parser); parser.nextToken(); return innerFromXContent(parser); } public static CreatePitResponse innerFromXContent(XContentParser parser) throws IOException { ensureExpectedToken(XContentParser.Token.FIELD_NAME, parser.currentToken(), parser); String currentFieldName = parser.currentName(); int successfulShards = -1; int totalShards = -1; int skippedShards = 0; int failedShards = 0; String id = null; long creationTime = 0; List failures = new ArrayList<>(); for (XContentParser.Token token = parser.nextToken(); token != XContentParser.Token.END_OBJECT; token = parser.nextToken()) { if (token == XContentParser.Token.FIELD_NAME) { currentFieldName = parser.currentName(); } else if (token.isValue()) { if (CREATION_TIME.match(currentFieldName, parser.getDeprecationHandler())) { creationTime = parser.longValue(); } else if (ID.match(currentFieldName, parser.getDeprecationHandler())) { id = parser.text(); } else { parser.skipChildren(); } } else if (token == XContentParser.Token.START_OBJECT) { if (RestActions._SHARDS_FIELD.match(currentFieldName, parser.getDeprecationHandler())) { while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { if (token == XContentParser.Token.FIELD_NAME) { currentFieldName = parser.currentName(); } else if (token.isValue()) { if (RestActions.FAILED_FIELD.match(currentFieldName, parser.getDeprecationHandler())) { failedShards = parser.intValue(); // we don't need it but need to consume it } else if (RestActions.SUCCESSFUL_FIELD.match(currentFieldName, parser.getDeprecationHandler())) { successfulShards = parser.intValue(); } else if (RestActions.TOTAL_FIELD.match(currentFieldName, parser.getDeprecationHandler())) { totalShards = parser.intValue(); } else if (RestActions.SKIPPED_FIELD.match(currentFieldName, parser.getDeprecationHandler())) { skippedShards = parser.intValue(); } else { parser.skipChildren(); } } else if (token == XContentParser.Token.START_ARRAY) { if (RestActions.FAILURES_FIELD.match(currentFieldName, parser.getDeprecationHandler())) { while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) { failures.add(ShardSearchFailure.fromXContent(parser)); } } else { parser.skipChildren(); } } else { parser.skipChildren(); } } } else { parser.skipChildren(); } } } return new CreatePitResponse( id, creationTime, totalShards, successfulShards, skippedShards, failedShards, failures.toArray(ShardSearchFailure.EMPTY_ARRAY) ); } public long getCreationTime() { return creationTime; } /** * The failed number of shards the search was executed on. */ public int getFailedShards() { return shardFailures.length; } /** * The failures that occurred during the search. */ public ShardSearchFailure[] getShardFailures() { return this.shardFailures; } @Override public void writeTo(StreamOutput out) throws IOException { out.writeString(id); out.writeVInt(totalShards); out.writeVInt(successfulShards); out.writeVInt(failedShards); out.writeVInt(skippedShards); out.writeLong(creationTime); out.writeVInt(shardFailures.length); for (ShardSearchFailure shardSearchFailure : shardFailures) { shardSearchFailure.writeTo(out); } } public String getId() { return id; } /** * The total number of shards the create pit operation was executed on. */ public int getTotalShards() { return totalShards; } /** * The successful number of shards the create pit operation was executed on. */ public int getSuccessfulShards() { return successfulShards; } public int getSkippedShards() { return skippedShards; } @Override public RestStatus status() { return RestStatus.status(successfulShards, totalShards, shardFailures); } }