2121
2222import org .elasticsearch .action .ActionResponse ;
2323import org .elasticsearch .common .Nullable ;
24+ import org .elasticsearch .common .ParseField ;
2425import org .elasticsearch .common .Strings ;
2526import org .elasticsearch .common .io .stream .StreamInput ;
2627import org .elasticsearch .common .io .stream .StreamOutput ;
2728import org .elasticsearch .common .unit .TimeValue ;
2829import org .elasticsearch .common .xcontent .StatusToXContentObject ;
2930import org .elasticsearch .common .xcontent .XContentBuilder ;
31+ import org .elasticsearch .common .xcontent .XContentParser ;
3032import org .elasticsearch .rest .RestStatus ;
3133import org .elasticsearch .rest .action .RestActions ;
3234import org .elasticsearch .search .SearchHits ;
3335import org .elasticsearch .search .aggregations .Aggregations ;
3436import org .elasticsearch .search .internal .InternalSearchResponse ;
3537import org .elasticsearch .search .profile .ProfileShardResult ;
38+ import org .elasticsearch .search .profile .SearchProfileShardResults ;
3639import org .elasticsearch .search .suggest .Suggest ;
3740
3841import java .io .IOException ;
42+ import java .util .ArrayList ;
43+ import java .util .List ;
3944import java .util .Map ;
4045
4146import static org .elasticsearch .action .search .ShardSearchFailure .readShardSearchFailure ;
47+ import static org .elasticsearch .common .xcontent .XContentParserUtils .ensureExpectedToken ;
48+ import static org .elasticsearch .common .xcontent .XContentParserUtils .throwUnknownField ;
49+ import static org .elasticsearch .common .xcontent .XContentParserUtils .throwUnknownToken ;
50+
4251
4352/**
4453 * A response of a search request.
4554 */
4655public class SearchResponse extends ActionResponse implements StatusToXContentObject {
4756
57+ private static final ParseField SCROLL_ID = new ParseField ("_scroll_id" );
58+ private static final ParseField TOOK = new ParseField ("took" );
59+ private static final ParseField TIMED_OUT = new ParseField ("timed_out" );
60+ private static final ParseField TERMINATED_EARLY = new ParseField ("terminated_early" );
61+ private static final ParseField NUM_REDUCE_PHASES = new ParseField ("num_reduce_phases" );
62+
4863 private SearchResponseSections internalResponse ;
4964
5065 private String scrollId ;
@@ -175,7 +190,8 @@ public void scrollId(String scrollId) {
175190 *
176191 * @return The profile results or an empty map
177192 */
178- @ Nullable public Map <String , ProfileShardResult > getProfileResults () {
193+ @ Nullable
194+ public Map <String , ProfileShardResult > getProfileResults () {
179195 return internalResponse .profile ();
180196 }
181197
@@ -189,22 +205,101 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws
189205
190206 public XContentBuilder innerToXContent (XContentBuilder builder , Params params ) throws IOException {
191207 if (scrollId != null ) {
192- builder .field ("_scroll_id" , scrollId );
208+ builder .field (SCROLL_ID . getPreferredName () , scrollId );
193209 }
194- builder .field ("took" , tookInMillis );
195- builder .field ("timed_out" , isTimedOut ());
210+ builder .field (TOOK . getPreferredName () , tookInMillis );
211+ builder .field (TIMED_OUT . getPreferredName () , isTimedOut ());
196212 if (isTerminatedEarly () != null ) {
197- builder .field ("terminated_early" , isTerminatedEarly ());
213+ builder .field (TERMINATED_EARLY . getPreferredName () , isTerminatedEarly ());
198214 }
199215 if (getNumReducePhases () != 1 ) {
200- builder .field ("num_reduce_phases" , getNumReducePhases ());
216+ builder .field (NUM_REDUCE_PHASES . getPreferredName () , getNumReducePhases ());
201217 }
202218 RestActions .buildBroadcastShardsHeader (builder , params , getTotalShards (), getSuccessfulShards (), getFailedShards (),
203219 getShardFailures ());
204220 internalResponse .toXContent (builder , params );
205221 return builder ;
206222 }
207223
224+ public static SearchResponse fromXContent (XContentParser parser ) throws IOException {
225+ ensureExpectedToken (XContentParser .Token .START_OBJECT , parser .currentToken (), parser ::getTokenLocation );
226+ XContentParser .Token token ;
227+ String currentFieldName = null ;
228+ SearchHits hits = null ;
229+ Aggregations aggs = null ;
230+ Suggest suggest = null ;
231+ SearchProfileShardResults profile = null ;
232+ boolean timedOut = false ;
233+ Boolean terminatedEarly = null ;
234+ int numReducePhases = 1 ;
235+ long tookInMillis = -1 ;
236+ int successfulShards = -1 ;
237+ int totalShards = -1 ;
238+ String scrollId = null ;
239+ List <ShardSearchFailure > failures = new ArrayList <>();
240+ while ((token = parser .nextToken ()) != XContentParser .Token .END_OBJECT ) {
241+ if (token == XContentParser .Token .FIELD_NAME ) {
242+ currentFieldName = parser .currentName ();
243+ } else if (token .isValue ()) {
244+ if (SCROLL_ID .match (currentFieldName )) {
245+ scrollId = parser .text ();
246+ } else if (TOOK .match (currentFieldName )) {
247+ tookInMillis = parser .longValue ();
248+ } else if (TIMED_OUT .match (currentFieldName )) {
249+ timedOut = parser .booleanValue ();
250+ } else if (TERMINATED_EARLY .match (currentFieldName )) {
251+ terminatedEarly = parser .booleanValue ();
252+ } else if (NUM_REDUCE_PHASES .match (currentFieldName )) {
253+ numReducePhases = parser .intValue ();
254+ } else {
255+ throwUnknownField (currentFieldName , parser .getTokenLocation ());
256+ }
257+ } else if (token == XContentParser .Token .START_OBJECT ) {
258+ if (SearchHits .Fields .HITS .equals (currentFieldName )) {
259+ hits = SearchHits .fromXContent (parser );
260+ } else if (Aggregations .AGGREGATIONS_FIELD .equals (currentFieldName )) {
261+ aggs = Aggregations .fromXContent (parser );
262+ } else if (Suggest .NAME .equals (currentFieldName )) {
263+ suggest = Suggest .fromXContent (parser );
264+ } else if (SearchProfileShardResults .PROFILE_FIELD .equals (currentFieldName )) {
265+ profile = SearchProfileShardResults .fromXContent (parser );
266+ } else if (RestActions ._SHARDS_FIELD .match (currentFieldName )) {
267+ while ((token = parser .nextToken ()) != XContentParser .Token .END_OBJECT ) {
268+ if (token == XContentParser .Token .FIELD_NAME ) {
269+ currentFieldName = parser .currentName ();
270+ } else if (token .isValue ()) {
271+ if (RestActions .FAILED_FIELD .match (currentFieldName )) {
272+ parser .intValue (); // we don't need it but need to consume it
273+ } else if (RestActions .SUCCESSFUL_FIELD .match (currentFieldName )) {
274+ successfulShards = parser .intValue ();
275+ } else if (RestActions .TOTAL_FIELD .match (currentFieldName )) {
276+ totalShards = parser .intValue ();
277+ } else {
278+ throwUnknownField (currentFieldName , parser .getTokenLocation ());
279+ }
280+ } else if (token == XContentParser .Token .START_ARRAY ) {
281+ if (RestActions .FAILURES_FIELD .match (currentFieldName )) {
282+ while ((token = parser .nextToken ()) != XContentParser .Token .END_ARRAY ) {
283+ failures .add (ShardSearchFailure .fromXContent (parser ));
284+ }
285+ } else {
286+ throwUnknownField (currentFieldName , parser .getTokenLocation ());
287+ }
288+ } else {
289+ throwUnknownToken (token , parser .getTokenLocation ());
290+ }
291+ }
292+ } else {
293+ throwUnknownField (currentFieldName , parser .getTokenLocation ());
294+ }
295+ }
296+ }
297+ SearchResponseSections searchResponseSections = new SearchResponseSections (hits , aggs , suggest , timedOut , terminatedEarly ,
298+ profile , numReducePhases );
299+ return new SearchResponse (searchResponseSections , scrollId , totalShards , successfulShards , tookInMillis ,
300+ failures .toArray (new ShardSearchFailure [failures .size ()]));
301+ }
302+
208303 @ Override
209304 public void readFrom (StreamInput in ) throws IOException {
210305 super .readFrom (in );
0 commit comments