-
Notifications
You must be signed in to change notification settings - Fork 24.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add rest highlevel explain API #31387
Conversation
Add explain API to rest high level client.
Pinging @elastic/es-core-infra |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I left some comments, thanks for your PR @PnPie !
Params params = new Params(request); | ||
params.withStoredFields(explainRequest.storedFields()); | ||
params.withFetchSourceContext(explainRequest.fetchSourceContext()); | ||
params.withRouting(explainRequest.routing()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
aren't we missing the parent parameter?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The parent
parameter is to set the routing
in fact, so here we can just use withRouting
to set it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Correct, we have removed support for parent in master. I will have to remember this when backporting to 6.x where we still support parent.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
scratch that, I think this will be fine as-is in 6.x as well.
@@ -826,6 +855,14 @@ protected final ElasticsearchStatusException parseResponseException(ResponseExce | |||
} | |||
} | |||
|
|||
private <Resp> Resp processResponse(Resp resp, int statusCode) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see the problem here: the response doesn't print out the exists flag, which should be assigned based on the status code. I would not check instanceof though here, I think it's possible to provide a context to the fromXContent method as an additional argument (currently declared Void so it is always null), make that a Boolean . It is not very easy though to make that work based on the status in the client. As an alternative, we could change the output of the API and make it print out the flag as well, like many other API do. @nik9000 what do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you make the responseConverter
something like
response -> {
ExplianResponse r = parseEntity(response.getEntity(), ExplainResponse::fromXContent);
r.setExists(convertExistsResponse(response));
return r;
}
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah! I see, you want the boolean as the context so you don't need to add setExists
. You could do that like this:
response -> {
CheckedFunction<XContentParser, Resp, IOException> entityParser = parser -> ExplainResponse.fromXContent(convertExistsResponse(response), parser);
return parseEntity(response.getEntity(), entityParser);
}
You make ExplainResponse.fromXContent like this instead of how you have it.
public static ExplainResponse fromXContent(boolean exists, XContentParser parser) {
return PARSER.apply(parser, exists);
}
I think that isn't too bad.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And you'd have to change the parser declaration to:
private static final ConstructingObjectParser<ExplainResponse, Boolean> PARSER = new ConstructingObjectParser<>("explain", true,
(args, exists) -> new ExplainResponse((String) args[0], (String) args[1], (String) args[2], exists, (Explanation) args[3],
+ (GetResult) arg[4]));
Or something like that. I'm not 100% sure on that bit to be honest, but somewhere in the building function you'd use exists
.
And then you can
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see it ! thx
PARSER.declareString(ConstructingObjectParser.constructorArg(), _INDEX); | ||
PARSER.declareString(ConstructingObjectParser.constructorArg(), _TYPE); | ||
PARSER.declareString(ConstructingObjectParser.constructorArg(), _ID); | ||
PARSER.declareBoolean(ConstructingObjectParser.constructorArg(), MATCHED); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that you end up passing matched in as exists in the ExplainResponse constructor.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I removed this as the exists
field is not necessary to be parsed through XContent using the way suggested by @nik9000
|
||
import static org.elasticsearch.common.lucene.Lucene.readExplanation; | ||
import static org.elasticsearch.common.lucene.Lucene.writeExplanation; | ||
|
||
/** | ||
* Response containing the score explanation. | ||
*/ | ||
public class ExplainResponse extends ActionResponse { | ||
public class ExplainResponse extends ActionResponse implements ToXContentObject { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you could make this StatusToXContentObject and have status method return the proper status based on the exists flag
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); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
does this parse the explanation recursively like it's printed out in toXcontent?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I put the explanationParser
recursively here https://github.com/elastic/elasticsearch/pull/31387/files#diff-86e50d6115fc7c8e90bfabdbb354886cR174 and the Explanation parsing works well as I tested in ExplainResponseTests.
} | ||
|
||
@Override | ||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that you can remove this code from RestExplainAction and call this one instead by using RestStatusToXContentListener
import static java.util.Collections.singletonMap; | ||
import static org.hamcrest.Matchers.equalTo; | ||
|
||
public class ExplainResponseTests extends AbstractStreamableXContentTestCase<ExplainResponse> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it is expected that injecting random fields causes problems in some parts of the response, for instance the section where the source is returned. I guess that some path needs to be excluded using getRandomFieldsExcludeFilter
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Params params = new Params(request); | ||
params.withStoredFields(explainRequest.storedFields()); | ||
params.withFetchSourceContext(explainRequest.fetchSourceContext()); | ||
params.withRouting(explainRequest.routing()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The parent
parameter is to set the routing
in fact, so here we can just use withRouting
to set it.
@@ -826,6 +855,14 @@ protected final ElasticsearchStatusException parseResponseException(ResponseExce | |||
} | |||
} | |||
|
|||
private <Resp> Resp processResponse(Resp resp, int statusCode) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see it ! thx
PARSER.declareString(ConstructingObjectParser.constructorArg(), _INDEX); | ||
PARSER.declareString(ConstructingObjectParser.constructorArg(), _TYPE); | ||
PARSER.declareString(ConstructingObjectParser.constructorArg(), _ID); | ||
PARSER.declareBoolean(ConstructingObjectParser.constructorArg(), MATCHED); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I removed this as the exists
field is not necessary to be parsed through XContent using the way suggested by @nik9000
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); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I put the explanationParser
recursively here https://github.com/elastic/elasticsearch/pull/31387/files#diff-86e50d6115fc7c8e90bfabdbb354886cR174 and the Explanation parsing works well as I tested in ExplainResponseTests.
test this please |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I left a couple of comments, this looks good though, thanks @PnPie !
|
||
@Override | ||
protected Predicate<String> getRandomFieldsExcludeFilter() { | ||
return field -> field.startsWith("fields") || field.startsWith("get"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that field.startsWith("get") could become field.startsWith("get._source") || field.startsWith("get.fields")
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @javanna I change this line to field.startsWith("get._source") || field.startsWith("get.fields")
but it still adds random items to "get.fields", it seems in this case it randomly inserts when field is "get" ?
Because when I do this field.startsWith("get._source") || field.startsWith("get.fields") || field.equals("get")
then it works well.
So I keep this as field.startsWith("get")
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good point, that is because GetResult prints out fields also at the get
level, depending on whether they are metadata fields or not. So any random field at the get
level will be parsed back as a field and printed out as part of fields
.
You can use field.equals("get") || field.startsWith("get.fields") || field.startsWith("get._source")
assertThat(expectedResponse, equalTo(generatedResponse)); | ||
} | ||
|
||
private ExplainResponse createSimpleResponse() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
given that we use this in a single place, can we remove this method and put the code where it's currently called?
return new ExplainResponse(index, type, id, exist, explanation, getResult); | ||
} | ||
|
||
private Explanation randomExplanation(Explanation... explanations) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can this be static?
-------------------------------------------------- | ||
|
||
[[java-rest-high-explain-request-optional]] | ||
===== Optional arguments |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that this should be at the same level as request. I noticed some other pages where we do it this way but I prefer to have them at the same level.
@@ -835,6 +877,174 @@ public void testRenderSearchTemplate() throws IOException { | |||
assertToXContentEquivalent(expectedSource, actualSource, XContentType.JSON); | |||
} | |||
|
|||
public void testExplainSimple() throws IOException { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would prefer not using Simple in a method name, although it's a test. Nothing is really simple :) Call it testExplain?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah definitly agree xD
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I left a couple more comments and replied to your latest one. Thanks for doing this @PnPie I will merge it as soon as the comments are addressed.
test this please |
Thx @javanna I updated it. |
retest this please |
@@ -1046,7 +1131,7 @@ private void indexSearchTestData() throws IOException { | |||
assertTrue(authorsResponse.isAcknowledged()); | |||
|
|||
CreateIndexRequest reviewersRequest = new CreateIndexRequest("contributors") | |||
.mapping("doc", "user", "type=keyword"); | |||
.mapping("doc", "user", "type=keyword,store=true"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
was this change necessary?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have tested the "stored_fields" flag in testExplain()
and I add an assertion here, if we don't "store" this field we will not get any values in GetResult
's fields
, so this assertion will not work. Otherwise we remove this assertion ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
got it, it's fine, I got confused by the github diff, it seemed like this was part of testFieldCaps only, but it's shared between different methods. All good!
thanks @PnPie ! |
* elastic/master: (57 commits) HLRest: Fix test for explain API [TEST] Fix RemoteClusterConnectionTests Add Create Snapshot to High-Level Rest Client (elastic#31215) Remove legacy MetaDataStateFormat (elastic#31603) Add explain API to high-level REST client (elastic#31387) Preserve thread context when connecting to remote cluster (elastic#31574) Unify headers for full text queries Remove redundant 'minimum_should_match' JDBC driver prepared statement set* methods (elastic#31494) [TEST] call yaml client close method from test suite (elastic#31591) ingest: Add ignore_missing property to foreach filter (elastic#22147) (elastic#31578) Fix a formatting issue in the docvalue_fields documentation. (elastic#31563) reduce log level at gradle configuration time [TEST] Close additional clients created while running yaml tests (elastic#31575) Docs: Clarify sensitive fields watcher encryption (elastic#31551) Watcher: Remove never executed code (elastic#31135) Add support for switching distribution for all integration tests (elastic#30874) Improve robustness of geo shape parser for malformed shapes (elastic#31449) QA: Create xpack yaml features (elastic#31403) Improve test times for tests using `RandomObjects::addFields` (elastic#31556) ...
@nik9000 thanks for catching and fixing, I will integrate your fix when backporting. |
* master: Docs: Remove duplicate test setup Print output when the name checker IT fails (#31660) Fix syntax errors in get-snapshots docs (#31656) Docs: Fix description of percentile ranks example example (#31652) Add MultiSearchTemplate support to High Level Rest client (#30836) Add test for low-level client round-robin behaviour (#31616) SQL: Refactor package names of sql-proto and sql-shared-proto projects (#31622) Remove deprecation warnings to prepare for Gradle 5 (sourceSets.main.output.classesDirs) (#30389) Correct integTest enable logic (#31646) Fix missing get-snapshots docs reference #31645 Do not check for Azure container existence (#31617) Merge AwsS3Service and InternalAwsS3Service in a S3Service class (#31580) Upgrade gradle wrapper to 4.8 (#31525) Only set vm.max_map_count if greater than default (#31512) Add Get Snapshots High Level REST API (#31537) QA: Merge query-builder-bwc to restart test (#30979) Update reindex.asciidoc (#31626) Docs: Skip xpack snippet tests if no xpack (#31619) mute CreateSnapshotRequestTests HLRest: Fix test for explain API [TEST] Fix RemoteClusterConnectionTests Add Create Snapshot to High-Level Rest Client (#31215) Remove legacy MetaDataStateFormat (#31603) Add explain API to high-level REST client (#31387) Preserve thread context when connecting to remote cluster (#31574) Unify headers for full text queries Remove redundant 'minimum_should_match' JDBC driver prepared statement set* methods (#31494) [TEST] call yaml client close method from test suite (#31591)
* 6.x: Do not check for object existence when deleting repository index files (#31680) Remove extra check for object existence in repository-gcs read object (#31661) [Test] Clean up some repository-s3 tests (#31601) Merge AzureStorageService and AzureStorageServiceImpl and clean up tests (#31607) [Docs] Use capital letters in section headings (#31678) [DOCS] Add PQL language Plugin (#31237) [DOCS] Fix licensing API details (#31667) Fix CreateSnapshotRequestTests Failure (#31630) Add Create Snapshot to High-Level Rest Client (#31215) Add MultiSearchTemplate support to High Level Rest client (#31662) SQL: Refactor package names of sql-proto and sql-shared-proto projects (#31622) [TEST] Mute failing NamingConventionsTaskIT tests [DOCS] Replace CONFIG_DIR with ES_PATH_CONF (#31635) Add test for low-level client round-robin behaviour (#31616) HLRest: Fix test for explain API Add explain API to high-level REST client (#31387)
Add explain API to rest high level client.
I create this PR firstly and I always have problem while parsing
GetReult
ofExplainResponse
through XContent. I usedGetResult.toXContentEmbedded/fromXContentEmbedded
and it works well inGetResultTests
, but inExplainResponseTests
seems it always fails with some arbitrary fields added in it. Anyone has any idea ?Relates to #27205