Render strucutred exception in multi search

MultiMatch still only returns the exception message but should return the
actual exception and render it in a structured fashion
This commit is contained in:
Simon Willnauer 2015-06-24 15:56:51 +02:00
parent 0ce18954a0
commit dc67bd0021
4 changed files with 44 additions and 10 deletions

View File

@ -20,6 +20,7 @@
package org.elasticsearch.action.search;
import com.google.common.collect.Iterators;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.action.ActionResponse;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.io.stream.StreamInput;
@ -31,6 +32,7 @@ import org.elasticsearch.common.xcontent.XContentBuilderString;
import org.elasticsearch.common.xcontent.XContentFactory;
import java.io.IOException;
import java.util.Collections;
import java.util.Iterator;
/**
@ -43,22 +45,22 @@ public class MultiSearchResponse extends ActionResponse implements Iterable<Mult
*/
public static class Item implements Streamable {
private SearchResponse response;
private String failureMessage;
private Throwable throwable;
Item() {
}
public Item(SearchResponse response, String failureMessage) {
public Item(SearchResponse response, Throwable throwable) {
this.response = response;
this.failureMessage = failureMessage;
this.throwable = throwable;
}
/**
* Is it a failed search?
*/
public boolean isFailure() {
return failureMessage != null;
return throwable != null;
}
/**
@ -66,7 +68,7 @@ public class MultiSearchResponse extends ActionResponse implements Iterable<Mult
*/
@Nullable
public String getFailureMessage() {
return failureMessage;
return throwable == null ? null : throwable.getMessage();
}
/**
@ -89,7 +91,7 @@ public class MultiSearchResponse extends ActionResponse implements Iterable<Mult
this.response = new SearchResponse();
response.readFrom(in);
} else {
failureMessage = in.readString();
throwable = in.readThrowable();
}
}
@ -100,9 +102,13 @@ public class MultiSearchResponse extends ActionResponse implements Iterable<Mult
response.writeTo(out);
} else {
out.writeBoolean(false);
out.writeString(failureMessage);
out.writeThrowable(throwable);
}
}
public Throwable getFailure() {
return throwable;
}
}
private Item[] items;
@ -150,7 +156,19 @@ public class MultiSearchResponse extends ActionResponse implements Iterable<Mult
for (Item item : items) {
if (item.isFailure()) {
builder.startObject();
builder.field(Fields.ERROR, item.getFailureMessage());
builder.startObject(Fields.ERROR);
final Throwable t = item.getFailure();
final ElasticsearchException[] rootCauses = ElasticsearchException.guessRootCauses(t);
builder.field(Fields.ROOT_CAUSE);
builder.startArray();
for (ElasticsearchException rootCause : rootCauses){
builder.startObject();
rootCause.toXContent(builder, new ToXContent.DelegatingMapParams(Collections.singletonMap(ElasticsearchException.REST_EXCEPTION_SKIP_CAUSE, "true"), params));
builder.endObject();
}
builder.endArray();
ElasticsearchException.toXContent(builder, params, t);
builder.endObject();
builder.endObject();
} else {
builder.startObject();
@ -165,6 +183,7 @@ public class MultiSearchResponse extends ActionResponse implements Iterable<Mult
static final class Fields {
static final XContentBuilderString RESPONSES = new XContentBuilderString("responses");
static final XContentBuilderString ERROR = new XContentBuilderString("error");
static final XContentBuilderString ROOT_CAUSE = new XContentBuilderString("root_cause");
}
@Override

View File

@ -69,7 +69,7 @@ public class TransportMultiSearchAction extends HandledTransportAction<MultiSear
@Override
public void onFailure(Throwable e) {
responses.set(index, new MultiSearchResponse.Item(null, ExceptionsHelper.detailedMessage(e)));
responses.set(index, new MultiSearchResponse.Item(null, e));
if (counter.decrementAndGet() == 0) {
finishHim();
}

View File

@ -21,9 +21,14 @@ package org.elasticsearch.action.search;
import org.elasticsearch.action.support.IndicesOptions;
import org.elasticsearch.common.io.Streams;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.test.ElasticsearchTestCase;
import org.junit.Test;
import java.io.IOException;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.nullValue;
@ -114,4 +119,12 @@ public class MultiSearchRequestTests extends ElasticsearchTestCase {
assertThat(request.requests().get(2).types()[1], equalTo("type1"));
assertThat(request.requests().get(2).routing(), equalTo("123"));
}
public void testResponseErrorToXContent() throws IOException {
MultiSearchResponse response = new MultiSearchResponse(new MultiSearchResponse.Item[]{new MultiSearchResponse.Item(null, new IllegalStateException("foobar")), new MultiSearchResponse.Item(null, new IllegalStateException("baaaaaazzzz"))});
XContentBuilder builder = XContentFactory.jsonBuilder();
response.toXContent(builder, ToXContent.EMPTY_PARAMS);
assertEquals("\"responses\"[{\"error\":{\"root_cause\":[{\"type\":\"illegal_state_exception\",\"reason\":\"foobar\"}],\"type\":\"illegal_state_exception\",\"reason\":\"foobar\"}},{\"error\":{\"root_cause\":[{\"type\":\"illegal_state_exception\",\"reason\":\"baaaaaazzzz\"}],\"type\":\"illegal_state_exception\",\"reason\":\"baaaaaazzzz\"}}]",
builder.string());
}
}

View File

@ -39,7 +39,9 @@
match: {foo: bar}
- match: { responses.0.hits.total: 3 }
- match: { responses.1.error: "/IndexMissingException.no.such.index./" }
- match: { responses.1.error.root_cause.0.type: index_missing_exception }
- match: { responses.1.error.root_cause.0.reason: "/no.such.index/" }
- match: { responses.1.error.root_cause.0.index: test_2 }
- match: { responses.2.hits.total: 1 }