mirror of
https://github.com/honeymoose/OpenSearch.git
synced 2025-02-17 10:25:15 +00:00
parent
b7246199db
commit
616703b880
@ -65,14 +65,15 @@ import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateReque
|
|||||||
import org.elasticsearch.action.admin.indices.validate.query.ValidateQueryRequest;
|
import org.elasticsearch.action.admin.indices.validate.query.ValidateQueryRequest;
|
||||||
import org.elasticsearch.action.bulk.BulkRequest;
|
import org.elasticsearch.action.bulk.BulkRequest;
|
||||||
import org.elasticsearch.action.delete.DeleteRequest;
|
import org.elasticsearch.action.delete.DeleteRequest;
|
||||||
|
import org.elasticsearch.action.explain.ExplainRequest;
|
||||||
import org.elasticsearch.action.fieldcaps.FieldCapabilitiesRequest;
|
import org.elasticsearch.action.fieldcaps.FieldCapabilitiesRequest;
|
||||||
import org.elasticsearch.action.get.GetRequest;
|
import org.elasticsearch.action.get.GetRequest;
|
||||||
import org.elasticsearch.action.get.MultiGetRequest;
|
import org.elasticsearch.action.get.MultiGetRequest;
|
||||||
import org.elasticsearch.action.index.IndexRequest;
|
import org.elasticsearch.action.index.IndexRequest;
|
||||||
import org.elasticsearch.action.ingest.DeletePipelineRequest;
|
import org.elasticsearch.action.ingest.DeletePipelineRequest;
|
||||||
import org.elasticsearch.action.ingest.PutPipelineRequest;
|
|
||||||
import org.elasticsearch.action.ingest.GetPipelineRequest;
|
import org.elasticsearch.action.ingest.GetPipelineRequest;
|
||||||
import org.elasticsearch.action.ingest.SimulatePipelineRequest;
|
import org.elasticsearch.action.ingest.SimulatePipelineRequest;
|
||||||
|
import org.elasticsearch.action.ingest.PutPipelineRequest;
|
||||||
import org.elasticsearch.action.search.ClearScrollRequest;
|
import org.elasticsearch.action.search.ClearScrollRequest;
|
||||||
import org.elasticsearch.action.search.MultiSearchRequest;
|
import org.elasticsearch.action.search.MultiSearchRequest;
|
||||||
import org.elasticsearch.action.search.SearchRequest;
|
import org.elasticsearch.action.search.SearchRequest;
|
||||||
@ -618,6 +619,19 @@ final class RequestConverters {
|
|||||||
return request;
|
return request;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Request explain(ExplainRequest explainRequest) throws IOException {
|
||||||
|
Request request = new Request(HttpGet.METHOD_NAME,
|
||||||
|
endpoint(explainRequest.index(), explainRequest.type(), explainRequest.id(), "_explain"));
|
||||||
|
|
||||||
|
Params params = new Params(request);
|
||||||
|
params.withStoredFields(explainRequest.storedFields());
|
||||||
|
params.withFetchSourceContext(explainRequest.fetchSourceContext());
|
||||||
|
params.withRouting(explainRequest.routing());
|
||||||
|
params.withPreference(explainRequest.preference());
|
||||||
|
request.setEntity(createEntity(explainRequest, REQUEST_BODY_CONTENT_TYPE));
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
static Request fieldCaps(FieldCapabilitiesRequest fieldCapabilitiesRequest) {
|
static Request fieldCaps(FieldCapabilitiesRequest fieldCapabilitiesRequest) {
|
||||||
Request request = new Request(HttpGet.METHOD_NAME, endpoint(fieldCapabilitiesRequest.indices(), "_field_caps"));
|
Request request = new Request(HttpGet.METHOD_NAME, endpoint(fieldCapabilitiesRequest.indices(), "_field_caps"));
|
||||||
|
|
||||||
|
@ -34,6 +34,8 @@ import org.elasticsearch.action.bulk.BulkRequest;
|
|||||||
import org.elasticsearch.action.bulk.BulkResponse;
|
import org.elasticsearch.action.bulk.BulkResponse;
|
||||||
import org.elasticsearch.action.delete.DeleteRequest;
|
import org.elasticsearch.action.delete.DeleteRequest;
|
||||||
import org.elasticsearch.action.delete.DeleteResponse;
|
import org.elasticsearch.action.delete.DeleteResponse;
|
||||||
|
import org.elasticsearch.action.explain.ExplainRequest;
|
||||||
|
import org.elasticsearch.action.explain.ExplainResponse;
|
||||||
import org.elasticsearch.action.fieldcaps.FieldCapabilitiesRequest;
|
import org.elasticsearch.action.fieldcaps.FieldCapabilitiesRequest;
|
||||||
import org.elasticsearch.action.fieldcaps.FieldCapabilitiesResponse;
|
import org.elasticsearch.action.fieldcaps.FieldCapabilitiesResponse;
|
||||||
import org.elasticsearch.action.get.GetRequest;
|
import org.elasticsearch.action.get.GetRequest;
|
||||||
@ -614,6 +616,42 @@ public class RestHighLevelClient implements Closeable {
|
|||||||
SearchTemplateResponse::fromXContent, listener, emptySet());
|
SearchTemplateResponse::fromXContent, listener, emptySet());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes a request using the Explain API.
|
||||||
|
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/search-explain.html">Explain API on elastic.co</a>
|
||||||
|
* @param explainRequest the request
|
||||||
|
* @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
|
||||||
|
* @return the response
|
||||||
|
* @throws IOException in case there is a problem sending the request or parsing back the response
|
||||||
|
*/
|
||||||
|
public final ExplainResponse explain(ExplainRequest explainRequest, RequestOptions options) throws IOException {
|
||||||
|
return performRequest(explainRequest, RequestConverters::explain, options,
|
||||||
|
response -> {
|
||||||
|
CheckedFunction<XContentParser, ExplainResponse, IOException> entityParser =
|
||||||
|
parser -> ExplainResponse.fromXContent(parser, convertExistsResponse(response));
|
||||||
|
return parseEntity(response.getEntity(), entityParser);
|
||||||
|
},
|
||||||
|
singleton(404));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asynchronously executes a request using the Explain API.
|
||||||
|
*
|
||||||
|
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/search-explain.html">Explain API on elastic.co</a>
|
||||||
|
* @param explainRequest the request
|
||||||
|
* @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
|
||||||
|
* @param listener the listener to be notified upon request completion
|
||||||
|
*/
|
||||||
|
public final void explainAsync(ExplainRequest explainRequest, RequestOptions options, ActionListener<ExplainResponse> listener) {
|
||||||
|
performRequestAsync(explainRequest, RequestConverters::explain, options,
|
||||||
|
response -> {
|
||||||
|
CheckedFunction<XContentParser, ExplainResponse, IOException> entityParser =
|
||||||
|
parser -> ExplainResponse.fromXContent(parser, convertExistsResponse(response));
|
||||||
|
return parseEntity(response.getEntity(), entityParser);
|
||||||
|
},
|
||||||
|
listener, singleton(404));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Executes a request using the Ranking Evaluation API.
|
* Executes a request using the Ranking Evaluation API.
|
||||||
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/search-rank-eval.html">Ranking Evaluation API
|
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/search-rank-eval.html">Ranking Evaluation API
|
||||||
|
@ -68,6 +68,7 @@ import org.elasticsearch.action.admin.indices.validate.query.ValidateQueryReques
|
|||||||
import org.elasticsearch.action.bulk.BulkRequest;
|
import org.elasticsearch.action.bulk.BulkRequest;
|
||||||
import org.elasticsearch.action.bulk.BulkShardRequest;
|
import org.elasticsearch.action.bulk.BulkShardRequest;
|
||||||
import org.elasticsearch.action.delete.DeleteRequest;
|
import org.elasticsearch.action.delete.DeleteRequest;
|
||||||
|
import org.elasticsearch.action.explain.ExplainRequest;
|
||||||
import org.elasticsearch.action.fieldcaps.FieldCapabilitiesRequest;
|
import org.elasticsearch.action.fieldcaps.FieldCapabilitiesRequest;
|
||||||
import org.elasticsearch.action.get.GetRequest;
|
import org.elasticsearch.action.get.GetRequest;
|
||||||
import org.elasticsearch.action.get.MultiGetRequest;
|
import org.elasticsearch.action.get.MultiGetRequest;
|
||||||
@ -111,6 +112,7 @@ import org.elasticsearch.common.xcontent.XContentParser;
|
|||||||
import org.elasticsearch.common.xcontent.XContentType;
|
import org.elasticsearch.common.xcontent.XContentType;
|
||||||
import org.elasticsearch.index.RandomCreateIndexGenerator;
|
import org.elasticsearch.index.RandomCreateIndexGenerator;
|
||||||
import org.elasticsearch.index.VersionType;
|
import org.elasticsearch.index.VersionType;
|
||||||
|
import org.elasticsearch.index.query.QueryBuilders;
|
||||||
import org.elasticsearch.index.query.TermQueryBuilder;
|
import org.elasticsearch.index.query.TermQueryBuilder;
|
||||||
import org.elasticsearch.index.rankeval.PrecisionAtK;
|
import org.elasticsearch.index.rankeval.PrecisionAtK;
|
||||||
import org.elasticsearch.index.rankeval.RankEvalRequest;
|
import org.elasticsearch.index.rankeval.RankEvalRequest;
|
||||||
@ -1418,6 +1420,49 @@ public class RequestConvertersTests extends ESTestCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testExplain() throws IOException {
|
||||||
|
String index = randomAlphaOfLengthBetween(3, 10);
|
||||||
|
String type = randomAlphaOfLengthBetween(3, 10);
|
||||||
|
String id = randomAlphaOfLengthBetween(3, 10);
|
||||||
|
|
||||||
|
ExplainRequest explainRequest = new ExplainRequest(index, type, id);
|
||||||
|
explainRequest.query(QueryBuilders.termQuery(randomAlphaOfLengthBetween(3, 10), randomAlphaOfLengthBetween(3, 10)));
|
||||||
|
|
||||||
|
Map<String, String> expectedParams = new HashMap<>();
|
||||||
|
|
||||||
|
if (randomBoolean()) {
|
||||||
|
String routing = randomAlphaOfLengthBetween(3, 10);
|
||||||
|
explainRequest.routing(routing);
|
||||||
|
expectedParams.put("routing", routing);
|
||||||
|
}
|
||||||
|
if (randomBoolean()) {
|
||||||
|
String preference = randomAlphaOfLengthBetween(3, 10);
|
||||||
|
explainRequest.preference(preference);
|
||||||
|
expectedParams.put("preference", preference);
|
||||||
|
}
|
||||||
|
if (randomBoolean()) {
|
||||||
|
String[] storedFields = generateRandomStringArray(10, 5, false);
|
||||||
|
String storedFieldsParams = randomFields(storedFields);
|
||||||
|
explainRequest.storedFields(storedFields);
|
||||||
|
expectedParams.put("stored_fields", storedFieldsParams);
|
||||||
|
}
|
||||||
|
if (randomBoolean()) {
|
||||||
|
randomizeFetchSourceContextParams(explainRequest::fetchSourceContext, expectedParams);
|
||||||
|
}
|
||||||
|
|
||||||
|
Request request = RequestConverters.explain(explainRequest);
|
||||||
|
StringJoiner endpoint = new StringJoiner("/", "/", "");
|
||||||
|
endpoint.add(index)
|
||||||
|
.add(type)
|
||||||
|
.add(id)
|
||||||
|
.add("_explain");
|
||||||
|
|
||||||
|
assertEquals(HttpGet.METHOD_NAME, request.getMethod());
|
||||||
|
assertEquals(endpoint.toString(), request.getEndpoint());
|
||||||
|
assertEquals(expectedParams, request.getParameters());
|
||||||
|
assertToXContentBody(explainRequest, request.getEntity());
|
||||||
|
}
|
||||||
|
|
||||||
public void testFieldCaps() {
|
public void testFieldCaps() {
|
||||||
// Create a random request.
|
// Create a random request.
|
||||||
String[] indices = randomIndicesNames(0, 5);
|
String[] indices = randomIndicesNames(0, 5);
|
||||||
|
@ -27,6 +27,8 @@ import org.apache.http.entity.StringEntity;
|
|||||||
import org.apache.http.nio.entity.NStringEntity;
|
import org.apache.http.nio.entity.NStringEntity;
|
||||||
import org.elasticsearch.ElasticsearchException;
|
import org.elasticsearch.ElasticsearchException;
|
||||||
import org.elasticsearch.ElasticsearchStatusException;
|
import org.elasticsearch.ElasticsearchStatusException;
|
||||||
|
import org.elasticsearch.action.explain.ExplainRequest;
|
||||||
|
import org.elasticsearch.action.explain.ExplainResponse;
|
||||||
import org.elasticsearch.action.fieldcaps.FieldCapabilities;
|
import org.elasticsearch.action.fieldcaps.FieldCapabilities;
|
||||||
import org.elasticsearch.action.fieldcaps.FieldCapabilitiesRequest;
|
import org.elasticsearch.action.fieldcaps.FieldCapabilitiesRequest;
|
||||||
import org.elasticsearch.action.fieldcaps.FieldCapabilitiesResponse;
|
import org.elasticsearch.action.fieldcaps.FieldCapabilitiesResponse;
|
||||||
@ -44,6 +46,7 @@ import org.elasticsearch.common.xcontent.XContentBuilder;
|
|||||||
import org.elasticsearch.common.xcontent.XContentFactory;
|
import org.elasticsearch.common.xcontent.XContentFactory;
|
||||||
import org.elasticsearch.common.xcontent.XContentType;
|
import org.elasticsearch.common.xcontent.XContentType;
|
||||||
import org.elasticsearch.index.query.MatchQueryBuilder;
|
import org.elasticsearch.index.query.MatchQueryBuilder;
|
||||||
|
import org.elasticsearch.index.query.QueryBuilders;
|
||||||
import org.elasticsearch.index.query.ScriptQueryBuilder;
|
import org.elasticsearch.index.query.ScriptQueryBuilder;
|
||||||
import org.elasticsearch.index.query.TermsQueryBuilder;
|
import org.elasticsearch.index.query.TermsQueryBuilder;
|
||||||
import org.elasticsearch.join.aggregations.Children;
|
import org.elasticsearch.join.aggregations.Children;
|
||||||
@ -63,6 +66,7 @@ import org.elasticsearch.search.aggregations.matrix.stats.MatrixStats;
|
|||||||
import org.elasticsearch.search.aggregations.matrix.stats.MatrixStatsAggregationBuilder;
|
import org.elasticsearch.search.aggregations.matrix.stats.MatrixStatsAggregationBuilder;
|
||||||
import org.elasticsearch.search.aggregations.support.ValueType;
|
import org.elasticsearch.search.aggregations.support.ValueType;
|
||||||
import org.elasticsearch.search.builder.SearchSourceBuilder;
|
import org.elasticsearch.search.builder.SearchSourceBuilder;
|
||||||
|
import org.elasticsearch.search.fetch.subphase.FetchSourceContext;
|
||||||
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
|
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
|
||||||
import org.elasticsearch.search.sort.SortOrder;
|
import org.elasticsearch.search.sort.SortOrder;
|
||||||
import org.elasticsearch.search.suggest.Suggest;
|
import org.elasticsearch.search.suggest.Suggest;
|
||||||
@ -135,7 +139,44 @@ public class SearchIT extends ESRestHighLevelClientTestCase {
|
|||||||
client().performRequest(HttpPut.METHOD_NAME, "/index3/doc/5", Collections.emptyMap(), doc);
|
client().performRequest(HttpPut.METHOD_NAME, "/index3/doc/5", Collections.emptyMap(), doc);
|
||||||
doc = new StringEntity("{\"field\":\"value2\"}", ContentType.APPLICATION_JSON);
|
doc = new StringEntity("{\"field\":\"value2\"}", ContentType.APPLICATION_JSON);
|
||||||
client().performRequest(HttpPut.METHOD_NAME, "/index3/doc/6", Collections.emptyMap(), doc);
|
client().performRequest(HttpPut.METHOD_NAME, "/index3/doc/6", Collections.emptyMap(), doc);
|
||||||
client().performRequest(HttpPost.METHOD_NAME, "/index1,index2,index3/_refresh");
|
|
||||||
|
mappings = new StringEntity(
|
||||||
|
"{" +
|
||||||
|
" \"mappings\": {" +
|
||||||
|
" \"doc\": {" +
|
||||||
|
" \"properties\": {" +
|
||||||
|
" \"field1\": {" +
|
||||||
|
" \"type\": \"keyword\"," +
|
||||||
|
" \"store\": true" +
|
||||||
|
" }," +
|
||||||
|
" \"field2\": {" +
|
||||||
|
" \"type\": \"keyword\"," +
|
||||||
|
" \"store\": true" +
|
||||||
|
" }" +
|
||||||
|
" }" +
|
||||||
|
" }" +
|
||||||
|
" }" +
|
||||||
|
"}}",
|
||||||
|
ContentType.APPLICATION_JSON);
|
||||||
|
client().performRequest(HttpPut.METHOD_NAME, "/index4", Collections.emptyMap(), mappings);
|
||||||
|
doc = new StringEntity("{\"field1\":\"value1\", \"field2\":\"value2\"}", ContentType.APPLICATION_JSON);
|
||||||
|
client().performRequest(HttpPut.METHOD_NAME, "/index4/doc/1", Collections.emptyMap(), doc);
|
||||||
|
StringEntity aliasFilter = new StringEntity(
|
||||||
|
"{" +
|
||||||
|
" \"actions\" : [" +
|
||||||
|
" {" +
|
||||||
|
" \"add\" : {" +
|
||||||
|
" \"index\" : \"index4\"," +
|
||||||
|
" \"alias\" : \"alias4\"," +
|
||||||
|
" \"filter\" : { \"term\" : { \"field2\" : \"value1\" } }" +
|
||||||
|
" }" +
|
||||||
|
" }" +
|
||||||
|
" ]" +
|
||||||
|
"}",
|
||||||
|
ContentType.APPLICATION_JSON);
|
||||||
|
client().performRequest(HttpPost.METHOD_NAME, "/_aliases", Collections.emptyMap(), aliasFilter);
|
||||||
|
|
||||||
|
client().performRequest(HttpPost.METHOD_NAME, "/index1,index2,index3,index4/_refresh");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testSearchNoQuery() throws IOException {
|
public void testSearchNoQuery() throws IOException {
|
||||||
@ -835,6 +876,174 @@ public class SearchIT extends ESRestHighLevelClientTestCase {
|
|||||||
assertToXContentEquivalent(expectedSource, actualSource, XContentType.JSON);
|
assertToXContentEquivalent(expectedSource, actualSource, XContentType.JSON);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testExplain() throws IOException {
|
||||||
|
{
|
||||||
|
ExplainRequest explainRequest = new ExplainRequest("index1", "doc", "1");
|
||||||
|
explainRequest.query(QueryBuilders.matchAllQuery());
|
||||||
|
|
||||||
|
ExplainResponse explainResponse = execute(explainRequest, highLevelClient()::explain, highLevelClient()::explainAsync);
|
||||||
|
|
||||||
|
assertThat(explainResponse.getIndex(), equalTo("index1"));
|
||||||
|
assertThat(explainResponse.getType(), equalTo("doc"));
|
||||||
|
assertThat(Integer.valueOf(explainResponse.getId()), equalTo(1));
|
||||||
|
assertTrue(explainResponse.isExists());
|
||||||
|
assertTrue(explainResponse.isMatch());
|
||||||
|
assertTrue(explainResponse.hasExplanation());
|
||||||
|
assertThat(explainResponse.getExplanation().getValue(), equalTo(1.0f));
|
||||||
|
assertNull(explainResponse.getGetResult());
|
||||||
|
}
|
||||||
|
{
|
||||||
|
ExplainRequest explainRequest = new ExplainRequest("index1", "doc", "1");
|
||||||
|
explainRequest.query(QueryBuilders.termQuery("field", "value1"));
|
||||||
|
|
||||||
|
ExplainResponse explainResponse = execute(explainRequest, highLevelClient()::explain, highLevelClient()::explainAsync);
|
||||||
|
|
||||||
|
assertThat(explainResponse.getIndex(), equalTo("index1"));
|
||||||
|
assertThat(explainResponse.getType(), equalTo("doc"));
|
||||||
|
assertThat(Integer.valueOf(explainResponse.getId()), equalTo(1));
|
||||||
|
assertTrue(explainResponse.isExists());
|
||||||
|
assertTrue(explainResponse.isMatch());
|
||||||
|
assertTrue(explainResponse.hasExplanation());
|
||||||
|
assertThat(explainResponse.getExplanation().getValue(), greaterThan(0.0f));
|
||||||
|
assertNull(explainResponse.getGetResult());
|
||||||
|
}
|
||||||
|
{
|
||||||
|
ExplainRequest explainRequest = new ExplainRequest("index1", "doc", "1");
|
||||||
|
explainRequest.query(QueryBuilders.termQuery("field", "value2"));
|
||||||
|
|
||||||
|
ExplainResponse explainResponse = execute(explainRequest, highLevelClient()::explain, highLevelClient()::explainAsync);
|
||||||
|
|
||||||
|
assertThat(explainResponse.getIndex(), equalTo("index1"));
|
||||||
|
assertThat(explainResponse.getType(), equalTo("doc"));
|
||||||
|
assertThat(Integer.valueOf(explainResponse.getId()), equalTo(1));
|
||||||
|
assertTrue(explainResponse.isExists());
|
||||||
|
assertFalse(explainResponse.isMatch());
|
||||||
|
assertTrue(explainResponse.hasExplanation());
|
||||||
|
assertNull(explainResponse.getGetResult());
|
||||||
|
}
|
||||||
|
{
|
||||||
|
ExplainRequest explainRequest = new ExplainRequest("index1", "doc", "1");
|
||||||
|
explainRequest.query(QueryBuilders.boolQuery()
|
||||||
|
.must(QueryBuilders.termQuery("field", "value1"))
|
||||||
|
.must(QueryBuilders.termQuery("field", "value2")));
|
||||||
|
|
||||||
|
ExplainResponse explainResponse = execute(explainRequest, highLevelClient()::explain, highLevelClient()::explainAsync);
|
||||||
|
|
||||||
|
assertThat(explainResponse.getIndex(), equalTo("index1"));
|
||||||
|
assertThat(explainResponse.getType(), equalTo("doc"));
|
||||||
|
assertThat(Integer.valueOf(explainResponse.getId()), equalTo(1));
|
||||||
|
assertTrue(explainResponse.isExists());
|
||||||
|
assertFalse(explainResponse.isMatch());
|
||||||
|
assertTrue(explainResponse.hasExplanation());
|
||||||
|
assertThat(explainResponse.getExplanation().getDetails().length, equalTo(2));
|
||||||
|
assertNull(explainResponse.getGetResult());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testExplainNonExistent() throws IOException {
|
||||||
|
{
|
||||||
|
ExplainRequest explainRequest = new ExplainRequest("non_existent_index", "doc", "1");
|
||||||
|
explainRequest.query(QueryBuilders.matchQuery("field", "value"));
|
||||||
|
ElasticsearchException exception = expectThrows(ElasticsearchException.class,
|
||||||
|
() -> execute(explainRequest, highLevelClient()::explain, highLevelClient()::explainAsync));
|
||||||
|
assertThat(exception.status(), equalTo(RestStatus.NOT_FOUND));
|
||||||
|
assertThat(exception.getIndex().getName(), equalTo("non_existent_index"));
|
||||||
|
assertThat(exception.getDetailedMessage(),
|
||||||
|
containsString("Elasticsearch exception [type=index_not_found_exception, reason=no such index]"));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
ExplainRequest explainRequest = new ExplainRequest("index1", "doc", "999");
|
||||||
|
explainRequest.query(QueryBuilders.matchQuery("field", "value1"));
|
||||||
|
|
||||||
|
ExplainResponse explainResponse = execute(explainRequest, highLevelClient()::explain, highLevelClient()::explainAsync);
|
||||||
|
|
||||||
|
assertThat(explainResponse.getIndex(), equalTo("index1"));
|
||||||
|
assertThat(explainResponse.getType(), equalTo("doc"));
|
||||||
|
assertThat(explainResponse.getId(), equalTo("999"));
|
||||||
|
assertFalse(explainResponse.isExists());
|
||||||
|
assertFalse(explainResponse.isMatch());
|
||||||
|
assertFalse(explainResponse.hasExplanation());
|
||||||
|
assertNull(explainResponse.getGetResult());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testExplainWithStoredFields() throws IOException {
|
||||||
|
{
|
||||||
|
ExplainRequest explainRequest = new ExplainRequest("index4", "doc", "1");
|
||||||
|
explainRequest.query(QueryBuilders.matchAllQuery());
|
||||||
|
explainRequest.storedFields(new String[]{"field1"});
|
||||||
|
|
||||||
|
ExplainResponse explainResponse = execute(explainRequest, highLevelClient()::explain, highLevelClient()::explainAsync);
|
||||||
|
|
||||||
|
assertTrue(explainResponse.isExists());
|
||||||
|
assertTrue(explainResponse.isMatch());
|
||||||
|
assertTrue(explainResponse.hasExplanation());
|
||||||
|
assertThat(explainResponse.getExplanation().getValue(), equalTo(1.0f));
|
||||||
|
assertTrue(explainResponse.getGetResult().isExists());
|
||||||
|
assertThat(explainResponse.getGetResult().getFields().keySet(), equalTo(Collections.singleton("field1")));
|
||||||
|
assertThat(explainResponse.getGetResult().getFields().get("field1").getValue().toString(), equalTo("value1"));
|
||||||
|
assertTrue(explainResponse.getGetResult().isSourceEmpty());
|
||||||
|
}
|
||||||
|
{
|
||||||
|
ExplainRequest explainRequest = new ExplainRequest("index4", "doc", "1");
|
||||||
|
explainRequest.query(QueryBuilders.matchAllQuery());
|
||||||
|
explainRequest.storedFields(new String[]{"field1", "field2"});
|
||||||
|
|
||||||
|
ExplainResponse explainResponse = execute(explainRequest, highLevelClient()::explain, highLevelClient()::explainAsync);
|
||||||
|
|
||||||
|
assertTrue(explainResponse.isExists());
|
||||||
|
assertTrue(explainResponse.isMatch());
|
||||||
|
assertTrue(explainResponse.hasExplanation());
|
||||||
|
assertThat(explainResponse.getExplanation().getValue(), equalTo(1.0f));
|
||||||
|
assertTrue(explainResponse.getGetResult().isExists());
|
||||||
|
assertThat(explainResponse.getGetResult().getFields().keySet().size(), equalTo(2));
|
||||||
|
assertThat(explainResponse.getGetResult().getFields().get("field1").getValue().toString(), equalTo("value1"));
|
||||||
|
assertThat(explainResponse.getGetResult().getFields().get("field2").getValue().toString(), equalTo("value2"));
|
||||||
|
assertTrue(explainResponse.getGetResult().isSourceEmpty());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testExplainWithFetchSource() throws IOException {
|
||||||
|
{
|
||||||
|
ExplainRequest explainRequest = new ExplainRequest("index4", "doc", "1");
|
||||||
|
explainRequest.query(QueryBuilders.matchAllQuery());
|
||||||
|
explainRequest.fetchSourceContext(new FetchSourceContext(true, new String[]{"field1"}, null));
|
||||||
|
|
||||||
|
ExplainResponse explainResponse = execute(explainRequest, highLevelClient()::explain, highLevelClient()::explainAsync);
|
||||||
|
|
||||||
|
assertTrue(explainResponse.isExists());
|
||||||
|
assertTrue(explainResponse.isMatch());
|
||||||
|
assertTrue(explainResponse.hasExplanation());
|
||||||
|
assertThat(explainResponse.getExplanation().getValue(), equalTo(1.0f));
|
||||||
|
assertTrue(explainResponse.getGetResult().isExists());
|
||||||
|
assertThat(explainResponse.getGetResult().getSource(), equalTo(Collections.singletonMap("field1", "value1")));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
ExplainRequest explainRequest = new ExplainRequest("index4", "doc", "1");
|
||||||
|
explainRequest.query(QueryBuilders.matchAllQuery());
|
||||||
|
explainRequest.fetchSourceContext(new FetchSourceContext(true, null, new String[] {"field2"}));
|
||||||
|
|
||||||
|
ExplainResponse explainResponse = execute(explainRequest, highLevelClient()::explain, highLevelClient()::explainAsync);
|
||||||
|
|
||||||
|
assertTrue(explainResponse.isExists());
|
||||||
|
assertTrue(explainResponse.isMatch());
|
||||||
|
assertTrue(explainResponse.hasExplanation());
|
||||||
|
assertThat(explainResponse.getExplanation().getValue(), equalTo(1.0f));
|
||||||
|
assertTrue(explainResponse.getGetResult().isExists());
|
||||||
|
assertThat(explainResponse.getGetResult().getSource(), equalTo(Collections.singletonMap("field1", "value1")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testExplainWithAliasFilter() throws IOException {
|
||||||
|
ExplainRequest explainRequest = new ExplainRequest("alias4", "doc", "1");
|
||||||
|
explainRequest.query(QueryBuilders.matchAllQuery());
|
||||||
|
|
||||||
|
ExplainResponse explainResponse = execute(explainRequest, highLevelClient()::explain, highLevelClient()::explainAsync);
|
||||||
|
|
||||||
|
assertTrue(explainResponse.isExists());
|
||||||
|
assertFalse(explainResponse.isMatch());
|
||||||
|
}
|
||||||
|
|
||||||
public void testFieldCaps() throws IOException {
|
public void testFieldCaps() throws IOException {
|
||||||
FieldCapabilitiesRequest request = new FieldCapabilitiesRequest()
|
FieldCapabilitiesRequest request = new FieldCapabilitiesRequest()
|
||||||
.indices("index1", "index2")
|
.indices("index1", "index2")
|
||||||
|
@ -19,12 +19,15 @@
|
|||||||
|
|
||||||
package org.elasticsearch.client.documentation;
|
package org.elasticsearch.client.documentation;
|
||||||
|
|
||||||
|
import org.apache.lucene.search.Explanation;
|
||||||
import org.elasticsearch.action.ActionListener;
|
import org.elasticsearch.action.ActionListener;
|
||||||
import org.elasticsearch.action.LatchedActionListener;
|
import org.elasticsearch.action.LatchedActionListener;
|
||||||
import org.elasticsearch.action.admin.indices.create.CreateIndexRequest;
|
import org.elasticsearch.action.admin.indices.create.CreateIndexRequest;
|
||||||
import org.elasticsearch.action.admin.indices.create.CreateIndexResponse;
|
import org.elasticsearch.action.admin.indices.create.CreateIndexResponse;
|
||||||
import org.elasticsearch.action.bulk.BulkRequest;
|
import org.elasticsearch.action.bulk.BulkRequest;
|
||||||
import org.elasticsearch.action.bulk.BulkResponse;
|
import org.elasticsearch.action.bulk.BulkResponse;
|
||||||
|
import org.elasticsearch.action.explain.ExplainRequest;
|
||||||
|
import org.elasticsearch.action.explain.ExplainResponse;
|
||||||
import org.elasticsearch.action.fieldcaps.FieldCapabilities;
|
import org.elasticsearch.action.fieldcaps.FieldCapabilities;
|
||||||
import org.elasticsearch.action.fieldcaps.FieldCapabilitiesRequest;
|
import org.elasticsearch.action.fieldcaps.FieldCapabilitiesRequest;
|
||||||
import org.elasticsearch.action.fieldcaps.FieldCapabilitiesResponse;
|
import org.elasticsearch.action.fieldcaps.FieldCapabilitiesResponse;
|
||||||
@ -47,10 +50,12 @@ import org.elasticsearch.client.Response;
|
|||||||
import org.elasticsearch.client.RestClient;
|
import org.elasticsearch.client.RestClient;
|
||||||
import org.elasticsearch.client.RestHighLevelClient;
|
import org.elasticsearch.client.RestHighLevelClient;
|
||||||
import org.elasticsearch.common.bytes.BytesReference;
|
import org.elasticsearch.common.bytes.BytesReference;
|
||||||
|
import org.elasticsearch.common.document.DocumentField;
|
||||||
import org.elasticsearch.common.text.Text;
|
import org.elasticsearch.common.text.Text;
|
||||||
import org.elasticsearch.common.unit.Fuzziness;
|
import org.elasticsearch.common.unit.Fuzziness;
|
||||||
import org.elasticsearch.common.unit.TimeValue;
|
import org.elasticsearch.common.unit.TimeValue;
|
||||||
import org.elasticsearch.common.xcontent.XContentType;
|
import org.elasticsearch.common.xcontent.XContentType;
|
||||||
|
import org.elasticsearch.index.get.GetResult;
|
||||||
import org.elasticsearch.index.query.MatchQueryBuilder;
|
import org.elasticsearch.index.query.MatchQueryBuilder;
|
||||||
import org.elasticsearch.index.query.QueryBuilder;
|
import org.elasticsearch.index.query.QueryBuilder;
|
||||||
import org.elasticsearch.index.query.QueryBuilders;
|
import org.elasticsearch.index.query.QueryBuilders;
|
||||||
@ -80,6 +85,7 @@ import org.elasticsearch.search.aggregations.bucket.terms.Terms.Bucket;
|
|||||||
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
|
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
|
||||||
import org.elasticsearch.search.aggregations.metrics.avg.Avg;
|
import org.elasticsearch.search.aggregations.metrics.avg.Avg;
|
||||||
import org.elasticsearch.search.builder.SearchSourceBuilder;
|
import org.elasticsearch.search.builder.SearchSourceBuilder;
|
||||||
|
import org.elasticsearch.search.fetch.subphase.FetchSourceContext;
|
||||||
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
|
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
|
||||||
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
|
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
|
||||||
import org.elasticsearch.search.profile.ProfileResult;
|
import org.elasticsearch.search.profile.ProfileResult;
|
||||||
@ -835,6 +841,85 @@ public class SearchDocumentationIT extends ESRestHighLevelClientTestCase {
|
|||||||
assertTrue(latch.await(30L, TimeUnit.SECONDS));
|
assertTrue(latch.await(30L, TimeUnit.SECONDS));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testExplain() throws Exception {
|
||||||
|
indexSearchTestData();
|
||||||
|
RestHighLevelClient client = highLevelClient();
|
||||||
|
|
||||||
|
// tag::explain-request
|
||||||
|
ExplainRequest request = new ExplainRequest("contributors", "doc", "1");
|
||||||
|
request.query(QueryBuilders.termQuery("user", "tanguy"));
|
||||||
|
// end::explain-request
|
||||||
|
|
||||||
|
// tag::explain-request-routing
|
||||||
|
request.routing("routing"); // <1>
|
||||||
|
// end::explain-request-routing
|
||||||
|
|
||||||
|
// tag::explain-request-preference
|
||||||
|
request.preference("_local"); // <1>
|
||||||
|
// end::explain-request-preference
|
||||||
|
|
||||||
|
// tag::explain-request-source
|
||||||
|
request.fetchSourceContext(new FetchSourceContext(true, new String[]{"user"}, null)); // <1>
|
||||||
|
// end::explain-request-source
|
||||||
|
|
||||||
|
// tag::explain-request-stored-field
|
||||||
|
request.storedFields(new String[]{"user"}); // <1>
|
||||||
|
// end::explain-request-stored-field
|
||||||
|
|
||||||
|
// tag::explain-execute
|
||||||
|
ExplainResponse response = client.explain(request, RequestOptions.DEFAULT);
|
||||||
|
// end::explain-execute
|
||||||
|
|
||||||
|
// tag::explain-response
|
||||||
|
String index = response.getIndex(); // <1>
|
||||||
|
String type = response.getType(); // <2>
|
||||||
|
String id = response.getId(); // <3>
|
||||||
|
boolean exists = response.isExists(); // <4>
|
||||||
|
boolean match = response.isMatch(); // <5>
|
||||||
|
boolean hasExplanation = response.hasExplanation(); // <6>
|
||||||
|
Explanation explanation = response.getExplanation(); // <7>
|
||||||
|
GetResult getResult = response.getGetResult(); // <8>
|
||||||
|
// end::explain-response
|
||||||
|
assertThat(index, equalTo("contributors"));
|
||||||
|
assertThat(type, equalTo("doc"));
|
||||||
|
assertThat(id, equalTo("1"));
|
||||||
|
assertTrue(exists);
|
||||||
|
assertTrue(match);
|
||||||
|
assertTrue(hasExplanation);
|
||||||
|
assertNotNull(explanation);
|
||||||
|
assertNotNull(getResult);
|
||||||
|
|
||||||
|
// tag::get-result
|
||||||
|
Map<String, Object> source = getResult.getSource(); // <1>
|
||||||
|
Map<String, DocumentField> fields = getResult.getFields(); // <2>
|
||||||
|
// end::get-result
|
||||||
|
assertThat(source, equalTo(Collections.singletonMap("user", "tanguy")));
|
||||||
|
assertThat(fields.get("user").getValue(), equalTo("tanguy"));
|
||||||
|
|
||||||
|
// tag::explain-execute-listener
|
||||||
|
ActionListener<ExplainResponse> listener = new ActionListener<ExplainResponse>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(ExplainResponse explainResponse) {
|
||||||
|
// <1>
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(Exception e) {
|
||||||
|
// <2>
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// end::explain-execute-listener
|
||||||
|
|
||||||
|
CountDownLatch latch = new CountDownLatch(1);
|
||||||
|
listener = new LatchedActionListener<>(listener, latch);
|
||||||
|
|
||||||
|
// tag::explain-execute-async
|
||||||
|
client.explainAsync(request, RequestOptions.DEFAULT, listener); // <1>
|
||||||
|
// end::explain-execute-async
|
||||||
|
|
||||||
|
assertTrue(latch.await(30L, TimeUnit.SECONDS));
|
||||||
|
}
|
||||||
|
|
||||||
public void testFieldCaps() throws Exception {
|
public void testFieldCaps() throws Exception {
|
||||||
indexSearchTestData();
|
indexSearchTestData();
|
||||||
RestHighLevelClient client = highLevelClient();
|
RestHighLevelClient client = highLevelClient();
|
||||||
@ -1046,7 +1131,7 @@ public class SearchDocumentationIT extends ESRestHighLevelClientTestCase {
|
|||||||
assertTrue(authorsResponse.isAcknowledged());
|
assertTrue(authorsResponse.isAcknowledged());
|
||||||
|
|
||||||
CreateIndexRequest reviewersRequest = new CreateIndexRequest("contributors")
|
CreateIndexRequest reviewersRequest = new CreateIndexRequest("contributors")
|
||||||
.mapping("doc", "user", "type=keyword");
|
.mapping("doc", "user", "type=keyword,store=true");
|
||||||
CreateIndexResponse reviewersResponse = highLevelClient().indices().create(reviewersRequest, RequestOptions.DEFAULT);
|
CreateIndexResponse reviewersResponse = highLevelClient().indices().create(reviewersRequest, RequestOptions.DEFAULT);
|
||||||
assertTrue(reviewersResponse.isAcknowledged());
|
assertTrue(reviewersResponse.isAcknowledged());
|
||||||
|
|
||||||
|
113
docs/java-rest/high-level/search/explain.asciidoc
Normal file
113
docs/java-rest/high-level/search/explain.asciidoc
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
[[java-rest-high-explain]]
|
||||||
|
=== Explain API
|
||||||
|
|
||||||
|
The explain api computes a score explanation for a query and a specific document.
|
||||||
|
This can give useful feedback whether a document matches or didn’t match a specific query.
|
||||||
|
|
||||||
|
[[java-rest-high-explain-request]]
|
||||||
|
==== Explain Request
|
||||||
|
|
||||||
|
An `ExplainRequest` expects an `index`, a `type` and an `id` to specify a certain document,
|
||||||
|
and a query represented by `QueryBuilder` to run against it (the way of <<java-rest-high-query-builders, building queries>>).
|
||||||
|
|
||||||
|
["source","java",subs="attributes,callouts,macros"]
|
||||||
|
--------------------------------------------------
|
||||||
|
include-tagged::{doc-tests}/SearchDocumentationIT.java[explain-request]
|
||||||
|
--------------------------------------------------
|
||||||
|
|
||||||
|
===== Optional arguments
|
||||||
|
|
||||||
|
["source","java",subs="attributes,callouts,macros"]
|
||||||
|
--------------------------------------------------
|
||||||
|
include-tagged::{doc-tests}/SearchDocumentationIT.java[explain-request-routing]
|
||||||
|
--------------------------------------------------
|
||||||
|
<1> Set a routing parameter
|
||||||
|
|
||||||
|
["source","java",subs="attributes,callouts,macros"]
|
||||||
|
--------------------------------------------------
|
||||||
|
include-tagged::{doc-tests}/SearchDocumentationIT.java[explain-request-preference]
|
||||||
|
--------------------------------------------------
|
||||||
|
<1> Use the preference parameter e.g. to execute the search to prefer local
|
||||||
|
shards. The default is to randomize across shards.
|
||||||
|
|
||||||
|
["source","java",subs="attributes,callouts,macros"]
|
||||||
|
--------------------------------------------------
|
||||||
|
include-tagged::{doc-tests}/SearchDocumentationIT.java[explain-request-source]
|
||||||
|
--------------------------------------------------
|
||||||
|
<1> Set to true to retrieve the _source of the document explained. You can also
|
||||||
|
retrieve part of the document by using _source_include & _source_exclude
|
||||||
|
(see <<java-rest-high-document-get-request-optional-arguments, Get API>> for more details)
|
||||||
|
|
||||||
|
["source","java",subs="attributes,callouts,macros"]
|
||||||
|
--------------------------------------------------
|
||||||
|
include-tagged::{doc-tests}/SearchDocumentationIT.java[explain-request-stored-field]
|
||||||
|
--------------------------------------------------
|
||||||
|
<1> Allows to control which stored fields to return as part of the document explained
|
||||||
|
(requires the field to be stored separately in the mappings).
|
||||||
|
|
||||||
|
[[java-rest-high-explain-sync]]
|
||||||
|
==== Synchronous Execution
|
||||||
|
|
||||||
|
The `explain` method executes the request synchronously:
|
||||||
|
|
||||||
|
["source","java",subs="attributes,callouts,macros"]
|
||||||
|
--------------------------------------------------
|
||||||
|
include-tagged::{doc-tests}/SearchDocumentationIT.java[explain-execute]
|
||||||
|
--------------------------------------------------
|
||||||
|
|
||||||
|
[[java-rest-high-explain-async]]
|
||||||
|
==== Asynchronous Execution
|
||||||
|
|
||||||
|
The `explainAsync` method executes the request asynchronously,
|
||||||
|
calling the provided `ActionListener` when the response is ready:
|
||||||
|
|
||||||
|
["source","java",subs="attributes,callouts,macros"]
|
||||||
|
--------------------------------------------------
|
||||||
|
include-tagged::{doc-tests}/SearchDocumentationIT.java[explain-execute-async]
|
||||||
|
--------------------------------------------------
|
||||||
|
<1> The `ExplainRequest` to execute and the `ActionListener` to use when
|
||||||
|
the execution completes.
|
||||||
|
|
||||||
|
The asynchronous method does not block and returns immediately. Once the request
|
||||||
|
completes, the `ActionListener` is called back using the `onResponse` method
|
||||||
|
if the execution successfully completed or using the `onFailure` method if
|
||||||
|
it failed.
|
||||||
|
|
||||||
|
A typical listener for `ExplainResponse` is constructed as follows:
|
||||||
|
|
||||||
|
["source","java",subs="attributes,callouts,macros"]
|
||||||
|
--------------------------------------------------
|
||||||
|
include-tagged::{doc-tests}/SearchDocumentationIT.java[explain-execute-listener]
|
||||||
|
--------------------------------------------------
|
||||||
|
<1> Called when the execution is successfully completed.
|
||||||
|
<2> Called when the whole `FieldCapabilitiesRequest` fails.
|
||||||
|
|
||||||
|
[[java-rest-high-explain-response]]
|
||||||
|
==== ExplainResponse
|
||||||
|
|
||||||
|
The `ExplainResponse` contains the following information:
|
||||||
|
|
||||||
|
["source","java",subs="attributes,callouts,macros"]
|
||||||
|
--------------------------------------------------
|
||||||
|
include-tagged::{doc-tests}/SearchDocumentationIT.java[explain-response]
|
||||||
|
--------------------------------------------------
|
||||||
|
<1> The index name of the explained document.
|
||||||
|
<2> The type name of the explained document.
|
||||||
|
<3> The id of the explained document.
|
||||||
|
<4> Indicates whether or not the explained document exists.
|
||||||
|
<5> Indicates whether or not there is a match between the explained document and
|
||||||
|
the provided query (the `match` is retrieved from the lucene `Explanation` behind the scenes
|
||||||
|
if the lucene `Explanation` models a match, it returns `true`, otherwise it returns `false`).
|
||||||
|
<6> Indicates whether or not there exists a lucene `Explanation` for this request.
|
||||||
|
<7> Get the lucene `Explanation` object if there exists.
|
||||||
|
<8> Get the `GetResult` object if the `_source` or the stored fields are retrieved.
|
||||||
|
|
||||||
|
The `GetResult` contains two maps internally to store the fetched `_source` and stored fields.
|
||||||
|
You can use the following methods to get them:
|
||||||
|
|
||||||
|
["source","java",subs="attributes,callouts,macros"]
|
||||||
|
--------------------------------------------------
|
||||||
|
include-tagged::{doc-tests}/SearchDocumentationIT.java[get-result]
|
||||||
|
--------------------------------------------------
|
||||||
|
<1> Retrieve the `_source` as a map.
|
||||||
|
<2> Retrieve the specified stored fields as a map.
|
@ -35,6 +35,7 @@ The Java High Level REST Client supports the following Search APIs:
|
|||||||
* <<java-rest-high-multi-search>>
|
* <<java-rest-high-multi-search>>
|
||||||
* <<java-rest-high-field-caps>>
|
* <<java-rest-high-field-caps>>
|
||||||
* <<java-rest-high-rank-eval>>
|
* <<java-rest-high-rank-eval>>
|
||||||
|
* <<java-rest-high-explain>>
|
||||||
|
|
||||||
include::search/search.asciidoc[]
|
include::search/search.asciidoc[]
|
||||||
include::search/scroll.asciidoc[]
|
include::search/scroll.asciidoc[]
|
||||||
@ -42,6 +43,7 @@ include::search/multi-search.asciidoc[]
|
|||||||
include::search/search-template.asciidoc[]
|
include::search/search-template.asciidoc[]
|
||||||
include::search/field-caps.asciidoc[]
|
include::search/field-caps.asciidoc[]
|
||||||
include::search/rank-eval.asciidoc[]
|
include::search/rank-eval.asciidoc[]
|
||||||
|
include::search/explain.asciidoc[]
|
||||||
|
|
||||||
== Miscellaneous APIs
|
== Miscellaneous APIs
|
||||||
|
|
||||||
|
@ -22,9 +22,12 @@ package org.elasticsearch.action.explain;
|
|||||||
import org.elasticsearch.action.ActionRequestValidationException;
|
import org.elasticsearch.action.ActionRequestValidationException;
|
||||||
import org.elasticsearch.action.ValidateActions;
|
import org.elasticsearch.action.ValidateActions;
|
||||||
import org.elasticsearch.action.support.single.shard.SingleShardRequest;
|
import org.elasticsearch.action.support.single.shard.SingleShardRequest;
|
||||||
|
import org.elasticsearch.common.ParseField;
|
||||||
import org.elasticsearch.common.Strings;
|
import org.elasticsearch.common.Strings;
|
||||||
import org.elasticsearch.common.io.stream.StreamInput;
|
import org.elasticsearch.common.io.stream.StreamInput;
|
||||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||||
|
import org.elasticsearch.common.xcontent.ToXContentObject;
|
||||||
|
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||||
import org.elasticsearch.index.query.QueryBuilder;
|
import org.elasticsearch.index.query.QueryBuilder;
|
||||||
import org.elasticsearch.search.fetch.subphase.FetchSourceContext;
|
import org.elasticsearch.search.fetch.subphase.FetchSourceContext;
|
||||||
import org.elasticsearch.search.internal.AliasFilter;
|
import org.elasticsearch.search.internal.AliasFilter;
|
||||||
@ -34,7 +37,9 @@ import java.io.IOException;
|
|||||||
/**
|
/**
|
||||||
* Explain request encapsulating the explain query and document identifier to get an explanation for.
|
* Explain request encapsulating the explain query and document identifier to get an explanation for.
|
||||||
*/
|
*/
|
||||||
public class ExplainRequest extends SingleShardRequest<ExplainRequest> {
|
public class ExplainRequest extends SingleShardRequest<ExplainRequest> implements ToXContentObject {
|
||||||
|
|
||||||
|
private static final ParseField QUERY_FIELD = new ParseField("query");
|
||||||
|
|
||||||
private String type = "_all";
|
private String type = "_all";
|
||||||
private String id;
|
private String id;
|
||||||
@ -186,4 +191,12 @@ public class ExplainRequest extends SingleShardRequest<ExplainRequest> {
|
|||||||
out.writeOptionalWriteable(fetchSourceContext);
|
out.writeOptionalWriteable(fetchSourceContext);
|
||||||
out.writeVLong(nowInMillis);
|
out.writeVLong(nowInMillis);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||||
|
builder.startObject();
|
||||||
|
builder.field(QUERY_FIELD.getPreferredName(), query);
|
||||||
|
builder.endObject();
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,11 +21,19 @@ package org.elasticsearch.action.explain;
|
|||||||
|
|
||||||
import org.apache.lucene.search.Explanation;
|
import org.apache.lucene.search.Explanation;
|
||||||
import org.elasticsearch.action.ActionResponse;
|
import org.elasticsearch.action.ActionResponse;
|
||||||
|
import org.elasticsearch.common.ParseField;
|
||||||
import org.elasticsearch.common.io.stream.StreamInput;
|
import org.elasticsearch.common.io.stream.StreamInput;
|
||||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||||
|
import org.elasticsearch.common.xcontent.ConstructingObjectParser;
|
||||||
|
import org.elasticsearch.common.xcontent.StatusToXContentObject;
|
||||||
|
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||||
|
import org.elasticsearch.common.xcontent.XContentParser;
|
||||||
import org.elasticsearch.index.get.GetResult;
|
import org.elasticsearch.index.get.GetResult;
|
||||||
|
import org.elasticsearch.rest.RestStatus;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
import static org.elasticsearch.common.lucene.Lucene.readExplanation;
|
import static org.elasticsearch.common.lucene.Lucene.readExplanation;
|
||||||
import static org.elasticsearch.common.lucene.Lucene.writeExplanation;
|
import static org.elasticsearch.common.lucene.Lucene.writeExplanation;
|
||||||
@ -33,7 +41,17 @@ import static org.elasticsearch.common.lucene.Lucene.writeExplanation;
|
|||||||
/**
|
/**
|
||||||
* Response containing the score explanation.
|
* Response containing the score explanation.
|
||||||
*/
|
*/
|
||||||
public class ExplainResponse extends ActionResponse {
|
public class ExplainResponse extends ActionResponse implements StatusToXContentObject {
|
||||||
|
|
||||||
|
private static final ParseField _INDEX = new ParseField("_index");
|
||||||
|
private static final ParseField _TYPE = new ParseField("_type");
|
||||||
|
private static final ParseField _ID = new ParseField("_id");
|
||||||
|
private static final ParseField MATCHED = new ParseField("matched");
|
||||||
|
private static final ParseField EXPLANATION = new ParseField("explanation");
|
||||||
|
private static final ParseField VALUE = new ParseField("value");
|
||||||
|
private static final ParseField DESCRIPTION = new ParseField("description");
|
||||||
|
private static final ParseField DETAILS = new ParseField("details");
|
||||||
|
private static final ParseField GET = new ParseField("get");
|
||||||
|
|
||||||
private String index;
|
private String index;
|
||||||
private String type;
|
private String type;
|
||||||
@ -94,6 +112,11 @@ public class ExplainResponse extends ActionResponse {
|
|||||||
return getResult;
|
return getResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RestStatus status() {
|
||||||
|
return exists ? RestStatus.OK : RestStatus.NOT_FOUND;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void readFrom(StreamInput in) throws IOException {
|
public void readFrom(StreamInput in) throws IOException {
|
||||||
super.readFrom(in);
|
super.readFrom(in);
|
||||||
@ -129,4 +152,90 @@ public class ExplainResponse extends ActionResponse {
|
|||||||
getResult.writeTo(out);
|
getResult.writeTo(out);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final ConstructingObjectParser<ExplainResponse, Boolean> PARSER = new ConstructingObjectParser<>("explain", true,
|
||||||
|
(arg, exists) -> new ExplainResponse((String) arg[0], (String) arg[1], (String) arg[2], exists, (Explanation) arg[3],
|
||||||
|
(GetResult) arg[4]));
|
||||||
|
|
||||||
|
static {
|
||||||
|
PARSER.declareString(ConstructingObjectParser.constructorArg(), _INDEX);
|
||||||
|
PARSER.declareString(ConstructingObjectParser.constructorArg(), _TYPE);
|
||||||
|
PARSER.declareString(ConstructingObjectParser.constructorArg(), _ID);
|
||||||
|
final ConstructingObjectParser<Explanation, Boolean> explanationParser = new ConstructingObjectParser<>("explanation", true,
|
||||||
|
arg -> {
|
||||||
|
if ((float) arg[0] > 0) {
|
||||||
|
return Explanation.match((float) arg[0], (String) arg[1], (Collection<Explanation>) arg[2]);
|
||||||
|
} else {
|
||||||
|
return Explanation.noMatch((String) arg[1], (Collection<Explanation>) arg[2]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
explanationParser.declareFloat(ConstructingObjectParser.constructorArg(), VALUE);
|
||||||
|
explanationParser.declareString(ConstructingObjectParser.constructorArg(), DESCRIPTION);
|
||||||
|
explanationParser.declareObjectArray(ConstructingObjectParser.constructorArg(), explanationParser, DETAILS);
|
||||||
|
PARSER.declareObject(ConstructingObjectParser.optionalConstructorArg(), explanationParser, EXPLANATION);
|
||||||
|
PARSER.declareObject(ConstructingObjectParser.optionalConstructorArg(), (p, c) -> GetResult.fromXContentEmbedded(p), GET);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ExplainResponse fromXContent(XContentParser parser, boolean exists) {
|
||||||
|
return PARSER.apply(parser, exists);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||||
|
builder.startObject();
|
||||||
|
builder.field(_INDEX.getPreferredName(), index);
|
||||||
|
builder.field(_TYPE.getPreferredName(), type);
|
||||||
|
builder.field(_ID.getPreferredName(), id);
|
||||||
|
builder.field(MATCHED.getPreferredName(), isMatch());
|
||||||
|
if (hasExplanation()) {
|
||||||
|
builder.startObject(EXPLANATION.getPreferredName());
|
||||||
|
buildExplanation(builder, explanation);
|
||||||
|
builder.endObject();
|
||||||
|
}
|
||||||
|
if (getResult != null) {
|
||||||
|
builder.startObject(GET.getPreferredName());
|
||||||
|
getResult.toXContentEmbedded(builder, params);
|
||||||
|
builder.endObject();
|
||||||
|
}
|
||||||
|
builder.endObject();
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void buildExplanation(XContentBuilder builder, Explanation explanation) throws IOException {
|
||||||
|
builder.field(VALUE.getPreferredName(), explanation.getValue());
|
||||||
|
builder.field(DESCRIPTION.getPreferredName(), explanation.getDescription());
|
||||||
|
Explanation[] innerExps = explanation.getDetails();
|
||||||
|
if (innerExps != null) {
|
||||||
|
builder.startArray(DETAILS.getPreferredName());
|
||||||
|
for (Explanation exp : innerExps) {
|
||||||
|
builder.startObject();
|
||||||
|
buildExplanation(builder, exp);
|
||||||
|
builder.endObject();
|
||||||
|
}
|
||||||
|
builder.endArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (obj == null || getClass() != obj.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ExplainResponse other = (ExplainResponse) obj;
|
||||||
|
return index.equals(other.index)
|
||||||
|
&& type.equals(other.type)
|
||||||
|
&& id.equals(other.id)
|
||||||
|
&& Objects.equals(explanation, other.explanation)
|
||||||
|
&& getResult.isExists() == other.getResult.isExists()
|
||||||
|
&& Objects.equals(getResult.sourceAsMap(), other.getResult.sourceAsMap())
|
||||||
|
&& Objects.equals(getResult.getFields(), other.getResult.getFields());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(index, type, id, explanation, getResult.isExists(), getResult.sourceAsMap(), getResult.getFields());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,30 +19,22 @@
|
|||||||
|
|
||||||
package org.elasticsearch.rest.action.search;
|
package org.elasticsearch.rest.action.search;
|
||||||
|
|
||||||
import org.apache.lucene.search.Explanation;
|
|
||||||
import org.elasticsearch.action.explain.ExplainRequest;
|
import org.elasticsearch.action.explain.ExplainRequest;
|
||||||
import org.elasticsearch.action.explain.ExplainResponse;
|
|
||||||
import org.elasticsearch.client.node.NodeClient;
|
import org.elasticsearch.client.node.NodeClient;
|
||||||
import org.elasticsearch.common.Strings;
|
import org.elasticsearch.common.Strings;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
|
||||||
import org.elasticsearch.index.get.GetResult;
|
|
||||||
import org.elasticsearch.index.query.QueryBuilder;
|
import org.elasticsearch.index.query.QueryBuilder;
|
||||||
import org.elasticsearch.rest.BaseRestHandler;
|
import org.elasticsearch.rest.BaseRestHandler;
|
||||||
import org.elasticsearch.rest.BytesRestResponse;
|
|
||||||
import org.elasticsearch.rest.RestController;
|
import org.elasticsearch.rest.RestController;
|
||||||
import org.elasticsearch.rest.RestRequest;
|
import org.elasticsearch.rest.RestRequest;
|
||||||
import org.elasticsearch.rest.RestResponse;
|
|
||||||
import org.elasticsearch.rest.action.RestActions;
|
import org.elasticsearch.rest.action.RestActions;
|
||||||
import org.elasticsearch.rest.action.RestBuilderListener;
|
import org.elasticsearch.rest.action.RestStatusToXContentListener;
|
||||||
import org.elasticsearch.search.fetch.subphase.FetchSourceContext;
|
import org.elasticsearch.search.fetch.subphase.FetchSourceContext;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
import static org.elasticsearch.rest.RestRequest.Method.GET;
|
import static org.elasticsearch.rest.RestRequest.Method.GET;
|
||||||
import static org.elasticsearch.rest.RestRequest.Method.POST;
|
import static org.elasticsearch.rest.RestRequest.Method.POST;
|
||||||
import static org.elasticsearch.rest.RestStatus.NOT_FOUND;
|
|
||||||
import static org.elasticsearch.rest.RestStatus.OK;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Rest action for computing a score explanation for specific documents.
|
* Rest action for computing a score explanation for specific documents.
|
||||||
@ -89,57 +81,6 @@ public class RestExplainAction extends BaseRestHandler {
|
|||||||
|
|
||||||
explainRequest.fetchSourceContext(FetchSourceContext.parseFromRestRequest(request));
|
explainRequest.fetchSourceContext(FetchSourceContext.parseFromRestRequest(request));
|
||||||
|
|
||||||
return channel -> client.explain(explainRequest, new RestBuilderListener<ExplainResponse>(channel) {
|
return channel -> client.explain(explainRequest, new RestStatusToXContentListener<>(channel));
|
||||||
@Override
|
|
||||||
public RestResponse buildResponse(ExplainResponse response, XContentBuilder builder) throws Exception {
|
|
||||||
builder.startObject();
|
|
||||||
builder.field(Fields._INDEX, response.getIndex())
|
|
||||||
.field(Fields._TYPE, response.getType())
|
|
||||||
.field(Fields._ID, response.getId())
|
|
||||||
.field(Fields.MATCHED, response.isMatch());
|
|
||||||
|
|
||||||
if (response.hasExplanation()) {
|
|
||||||
builder.startObject(Fields.EXPLANATION);
|
|
||||||
buildExplanation(builder, response.getExplanation());
|
|
||||||
builder.endObject();
|
|
||||||
}
|
|
||||||
GetResult getResult = response.getGetResult();
|
|
||||||
if (getResult != null) {
|
|
||||||
builder.startObject(Fields.GET);
|
|
||||||
response.getGetResult().toXContentEmbedded(builder, request);
|
|
||||||
builder.endObject();
|
|
||||||
}
|
|
||||||
builder.endObject();
|
|
||||||
return new BytesRestResponse(response.isExists() ? OK : NOT_FOUND, builder);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void buildExplanation(XContentBuilder builder, Explanation explanation) throws IOException {
|
|
||||||
builder.field(Fields.VALUE, explanation.getValue());
|
|
||||||
builder.field(Fields.DESCRIPTION, explanation.getDescription());
|
|
||||||
Explanation[] innerExps = explanation.getDetails();
|
|
||||||
if (innerExps != null) {
|
|
||||||
builder.startArray(Fields.DETAILS);
|
|
||||||
for (Explanation exp : innerExps) {
|
|
||||||
builder.startObject();
|
|
||||||
buildExplanation(builder, exp);
|
|
||||||
builder.endObject();
|
|
||||||
}
|
|
||||||
builder.endArray();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
static class Fields {
|
|
||||||
static final String _INDEX = "_index";
|
|
||||||
static final String _TYPE = "_type";
|
|
||||||
static final String _ID = "_id";
|
|
||||||
static final String MATCHED = "matched";
|
|
||||||
static final String EXPLANATION = "explanation";
|
|
||||||
static final String VALUE = "value";
|
|
||||||
static final String DESCRIPTION = "description";
|
|
||||||
static final String DETAILS = "details";
|
|
||||||
static final String GET = "get";
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,9 +16,8 @@
|
|||||||
* specific language governing permissions and limitations
|
* specific language governing permissions and limitations
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
package org.elasticsearch.action;
|
package org.elasticsearch.action.explain;
|
||||||
|
|
||||||
import org.elasticsearch.action.explain.ExplainRequest;
|
|
||||||
import org.elasticsearch.common.io.stream.BytesStreamOutput;
|
import org.elasticsearch.common.io.stream.BytesStreamOutput;
|
||||||
import org.elasticsearch.common.io.stream.NamedWriteableAwareStreamInput;
|
import org.elasticsearch.common.io.stream.NamedWriteableAwareStreamInput;
|
||||||
import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
|
import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
|
@ -0,0 +1,127 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to Elasticsearch under one or more contributor
|
||||||
|
* license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright
|
||||||
|
* ownership. Elasticsearch licenses this file to you under
|
||||||
|
* the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
* not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.elasticsearch.action.explain;
|
||||||
|
|
||||||
|
import org.apache.lucene.search.Explanation;
|
||||||
|
import org.elasticsearch.common.bytes.BytesArray;
|
||||||
|
import org.elasticsearch.common.bytes.BytesReference;
|
||||||
|
import org.elasticsearch.common.document.DocumentField;
|
||||||
|
import org.elasticsearch.common.xcontent.ToXContent;
|
||||||
|
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||||
|
import org.elasticsearch.common.xcontent.XContentFactory;
|
||||||
|
import org.elasticsearch.common.xcontent.XContentParser;
|
||||||
|
import org.elasticsearch.common.xcontent.XContentType;
|
||||||
|
import org.elasticsearch.index.get.GetResult;
|
||||||
|
import org.elasticsearch.test.AbstractStreamableXContentTestCase;
|
||||||
|
import org.elasticsearch.test.RandomObjects;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
import static java.util.Collections.singletonList;
|
||||||
|
import static java.util.Collections.singletonMap;
|
||||||
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
|
|
||||||
|
public class ExplainResponseTests extends AbstractStreamableXContentTestCase<ExplainResponse> {
|
||||||
|
@Override
|
||||||
|
protected ExplainResponse doParseInstance(XContentParser parser) throws IOException {
|
||||||
|
return ExplainResponse.fromXContent(parser, randomBoolean());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ExplainResponse createBlankInstance() {
|
||||||
|
return new ExplainResponse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ExplainResponse createTestInstance() {
|
||||||
|
String index = randomAlphaOfLength(5);
|
||||||
|
String type = randomAlphaOfLength(5);
|
||||||
|
String id = String.valueOf(randomIntBetween(1,100));
|
||||||
|
boolean exist = randomBoolean();
|
||||||
|
Explanation explanation = randomExplanation(randomExplanation(randomExplanation()), randomExplanation());
|
||||||
|
String fieldName = randomAlphaOfLength(10);
|
||||||
|
List<Object> values = Arrays.asList(randomAlphaOfLengthBetween(3, 10), randomInt(), randomLong(), randomDouble(), randomBoolean());
|
||||||
|
GetResult getResult = new GetResult(randomAlphaOfLengthBetween(3, 10),
|
||||||
|
randomAlphaOfLengthBetween(3, 10),
|
||||||
|
randomAlphaOfLengthBetween(3, 10),
|
||||||
|
randomNonNegativeLong(),
|
||||||
|
true,
|
||||||
|
RandomObjects.randomSource(random()),
|
||||||
|
singletonMap(fieldName, new DocumentField(fieldName, values)));
|
||||||
|
return new ExplainResponse(index, type, id, exist, explanation, getResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Predicate<String> getRandomFieldsExcludeFilter() {
|
||||||
|
return field -> field.equals("get") || field.startsWith("get.fields") || field.startsWith("get._source");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testToXContent() throws IOException {
|
||||||
|
String index = "index";
|
||||||
|
String type = "type";
|
||||||
|
String id = "1";
|
||||||
|
boolean exist = true;
|
||||||
|
Explanation explanation = Explanation.match(1.0f, "description", Collections.emptySet());
|
||||||
|
GetResult getResult = new GetResult(null, null, null, -1, true, new BytesArray("{ \"field1\" : " +
|
||||||
|
"\"value1\", \"field2\":\"value2\"}"), singletonMap("field1", new DocumentField("field1",
|
||||||
|
singletonList("value1"))));
|
||||||
|
ExplainResponse response = new ExplainResponse(index, type, id, exist, explanation, getResult);
|
||||||
|
|
||||||
|
XContentBuilder builder = XContentFactory.contentBuilder(XContentType.JSON);
|
||||||
|
response.toXContent(builder, ToXContent.EMPTY_PARAMS);
|
||||||
|
|
||||||
|
String generatedResponse = BytesReference.bytes(builder).utf8ToString().replaceAll("\\s+", "");
|
||||||
|
|
||||||
|
String expectedResponse =
|
||||||
|
("{\n" +
|
||||||
|
" \"_index\":\"index\",\n" +
|
||||||
|
" \"_type\":\"type\",\n" +
|
||||||
|
" \"_id\":\"1\",\n" +
|
||||||
|
" \"matched\":true,\n" +
|
||||||
|
" \"explanation\":{\n" +
|
||||||
|
" \"value\":1.0,\n" +
|
||||||
|
" \"description\":\"description\",\n" +
|
||||||
|
" \"details\":[]\n" +
|
||||||
|
" },\n" +
|
||||||
|
" \"get\":{\n" +
|
||||||
|
" \"found\":true,\n" +
|
||||||
|
" \"_source\":{\n" +
|
||||||
|
" \"field1\":\"value1\",\n" +
|
||||||
|
" \"field2\":\"value2\"\n" +
|
||||||
|
" },\n" +
|
||||||
|
" \"fields\":{\n" +
|
||||||
|
" \"field1\":[\n" +
|
||||||
|
" \"value1\"\n" +
|
||||||
|
" ]\n" +
|
||||||
|
" }\n" +
|
||||||
|
" }\n" +
|
||||||
|
"}").replaceAll("\\s+", "");
|
||||||
|
assertThat(expectedResponse, equalTo(generatedResponse));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Explanation randomExplanation(Explanation... explanations) {
|
||||||
|
return Explanation.match(randomFloat(), randomAlphaOfLengthBetween(1, 10),
|
||||||
|
explanations.length > 0 ? explanations : new Explanation[0]);
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user