Skip to content

Conversation

@cbuescher
Copy link
Member

In preparation for being able to parse SearchResponse from its rest representation
for the java rest client, this adds fromXContent to SearchProfileShardResults and its
nested classes.

@cbuescher cbuescher added :Search/Search Search-related issues that do not fall into other categories >enhancement review v6.0.0-alpha1 labels Jan 17, 2017
@cbuescher cbuescher requested review from javanna and tlrx January 17, 2017 09:12
@cbuescher cbuescher force-pushed the addParsing-ProfileResult branch from 663aa6f to 7cf041a Compare January 17, 2017 11:20
Copy link
Member

@javanna javanna left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

left a few minors, thanks for doing this

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What causes this if? differences between the different content types that we support?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should rather do this while parsing instead of using parser.map() here?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And use java.lang.Number?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: can you replace this with the same comment that we had above? I find it more effective and clear

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bad merge?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not using XContentHelper#toXContent here too?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice, this method could be potentially called in all our tests so that we randomize the human readable flag, just to make sure we don't miss any of those additional fields. I think we haven't and this is the first time we encounter this but tests are the only way to actually verify. Maybe we can do this as a followup?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

++ good point

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you look into this when you have time please?

Copy link
Member

@tlrx tlrx left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @cbuescher, I'm sure that it hasn't been fun to do. I left some comments here and there, nothing big.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And use java.lang.Number?

}
}
}
Map<String, Long> timings = new HashMap<>(parsedTimings.size());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we check that parsedTimings is not null before doing this?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This part has been removed while addressing another comment.

* A map of breakdown timing for the node
* @return The total time at this node, inclusive of children
*/
private static long getNodeTime(Map<String, Long> timings) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if timings is null? It doesn't seem to be required in the ctor

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, I moved this code from somewhere where it was clear that the map is always there. I will add a check to the ctor.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! Could we also rename this to getTotalTime or something?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason I left the name was to make it clearer where the code was before in this PR (its from AbstractInternalProfileTree). I will rename it to getTotalTime. Also I think the comment is not up to date anymore (it doesn't sum any children) so I will remove that part)

ensureExpectedToken(XContentParser.Token.START_OBJECT, token, parser::getTokenLocation);
List<QueryProfileShardResult> queryProfileResults = new ArrayList<>();
AggregationProfileShardResult aggProfileShardResult = null;
String name = null;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this be renamed to id to avoid confusion?

} else {
throwUnknownField(currentFieldName, parser.getTokenLocation());
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe add another else block with unexpected token type?

} else {
throwUnknownField(currentFieldName, parser.getTokenLocation());
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here, shouldn't we throw an exception if the token type is not expected? Or maybe I'm missing something.

} else {
throwUnknownField(currentFieldName, parser.getTokenLocation());
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe also here

XContentType xcontentType = randomFrom(XContentType.values());
XContentBuilder builder = XContentFactory.contentBuilder(xcontentType);
builder.startObject();
builder = shardResult.toXContent(builder, ToXContent.EMPTY_PARAMS);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't we use XContentHelper.toXContent() here?

@cbuescher cbuescher force-pushed the addParsing-ProfileResult branch from 92d0e1e to fabf41b Compare January 17, 2017 21:09
@cbuescher cbuescher force-pushed the addParsing-ProfileResult branch from fabf41b to 4029cda Compare January 17, 2017 21:16
@cbuescher
Copy link
Member Author

@tlrx @javanna thanks for the review, I pushed a commit that hopefully addresses all your comments.

XContentParser.Token token = parser.currentToken();
ensureExpectedToken(XContentParser.Token.START_OBJECT, token, parser::getTokenLocation);
Map<String, ProfileShardResult> searchProfileResults = new HashMap<>();
Map<String, ProfileShardResult> searchProfileResults = new LinkedHashMap<>();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we need ordering here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This way we preseve the order of the shard keys we encountered during xContent parsing. Without preserving this order, I encountered different xContent rendering outputs for the "shards" section (which is an array) in the roundtrip tests, so the order matters (at least the way assertToXContentEquivalent works so far).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I pushed another commit that fixes the order in which the elements in the "shards" array are written out.

Copy link
Member

@tlrx tlrx left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the hard work! I made many comments but they are all cosmetics or minor. This is getting very close

* A map of breakdown timing for the node
* @return The total time at this node, inclusive of children
*/
private static long getNodeTime(Map<String, Long> timings) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! Could we also rename this to getTotalTime or something?

} else if (DESCRIPTION.match(currentFieldName)) {
description = parser.text();
} else if (NODE_TIME.match(currentFieldName)) {
// we need to consume this value, but we use the raw nanosecond value
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the "but we use the raw nanosecond value" part of this comment is misleading. When I read it I thought we used the NODE_TIME_RAW but it doesn't seems to be the case

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, we sum the timings values in ProfileResult ctor now, no need for both of these values, I'll update the comments

String currentFieldName = null;
List<ProfileResult> queryProfileResults = new ArrayList<>();
long rewriteTime = 0;
CollectorResult profileCollector = null;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe profileCollector -> collector to reflect the field's name?

try (XContentParser parser = createParser(xContentType.xContent(), originalBytes)) {
ensureExpectedToken(XContentParser.Token.START_OBJECT, parser.nextToken(), parser::getTokenLocation);
ProfileResult parsed = ProfileResult.fromXContent(parser);
assertEquals(profileResult.getTime(), parsed.getTime());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we check the parser in the try/catch block, and the parsed ProfileResult after that?

It's basically moving those two lines:
ProfileResult parsed = ProfileResult.fromXContent(parser);
assertEquals(profileResult.getTime(), parsed.getTime());

after the try block.

timings.put("key1", 12345L);
timings.put("key2", 6789L);
ProfileResult result = new ProfileResult("someType", "some description", timings, children, 123456L);
children.add(new ProfileResult("child1", "desc1", timings1, Collections.emptyList()));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose that you can use singletonMap("key1", 100L) directly here

XContentParserUtils.ensureFieldName(parser, parser.nextToken(), AggregationProfileShardResult.AGGREGATIONS);
XContentParserUtils.ensureExpectedToken(parser.nextToken(), XContentParser.Token.START_ARRAY, parser::getTokenLocation);
AggregationProfileShardResult parsed = AggregationProfileShardResult.fromXContent(parser);
assertToXContentEquivalent(builder.bytes(), toXContent(parsed, xcontentType), xcontentType);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we move this down, after the Token checks?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

ProfileResult profileResult = new ProfileResult("someType", "someDescription", timings, Collections.emptyList());
profileResults.add(profileResult);
AggregationProfileShardResult aggProfileResults = new AggregationProfileShardResult(profileResults);
XContentBuilder builder = JsonXContent.contentBuilder();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here, try with resource block

CollectorResult collectorResult = createTestItem(1);
XContentType xcontentType = randomFrom(XContentType.values());
XContentBuilder builder = XContentFactory.contentBuilder(xcontentType);
boolean humanReadable = randomBoolean();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to use an intermediate variable for this

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I need it later when comparing to the xContent rendering of the parsed object.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I meant builder.humanReadable(randomBoolean()); here and use builder.humanReadable() later

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, that doesn't work after my last changes because I got rid of the builder.

queryProfileResults.add(ProfileResultTests.createTestItem(1));
}
CollectorResult profileCollector = CollectorResultTests.createTestItem(2);
long rewriteTime = randomNonNegativeLong();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we use the %100_000 strategy here too?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, good point.

public void testFromXContent() throws IOException {
QueryProfileShardResult profileResult = createTestItem();
XContentType xcontentType = randomFrom(XContentType.values());
XContentBuilder builder = XContentFactory.contentBuilder(xcontentType);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here


for (Map.Entry<String, ProfileShardResult> entry : shardResults.entrySet()) {
builder.startObject(PROFILE_FIELD).startArray(SHARDS_FIELD);
TreeSet<String> sortedKeys = new TreeSet<>(shardResults.keySet());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we forced to sort all shard results every time just to be able to check them in tests? Can't we check / sort again in the test instead?

Copy link
Member Author

@cbuescher cbuescher Jan 18, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I talked to @javanna about this, the idea was to also make the rendered response reproducibly the same for the same source object. Happy to change this back any time, but the overhead this poses is negligible I think, also its just performed once per response.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it is not just for tests. Unfortunately this object uses a map to store information that goes into a json array, which is by definition ordered. Either we change the format of the response or we guarantee ordering then. I think this is a good way to guarantee ordering and make the response more human readable too.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe add a comment to explain this @cbuescher ?

@cbuescher
Copy link
Member Author

@tlrx thanks for reviewing, I added a commit that addresses most of your comments and left some answer where I'd like to keep things (or discuss further)

Copy link
Member

@javanna javanna left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Copy link
Member

@tlrx tlrx left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@cbuescher cbuescher force-pushed the addParsing-ProfileResult branch from 0ef1ad1 to 1be3216 Compare January 19, 2017 14:01
@cbuescher cbuescher merged commit e035540 into elastic:master Jan 19, 2017
cbuescher added a commit that referenced this pull request Jan 19, 2017
…sses (#22649)

In preparation for being able to parse SearchResponse from its rest representation
for the java rest client, this adds fromXContent to SearchProfileShardResults and its
nested classes.
@cbuescher
Copy link
Member Author

Ported to 5.x with c8e027f

jasontedor added a commit to jasontedor/elasticsearch that referenced this pull request Jan 20, 2017
* master: (117 commits)
  Add missing serialization BWC for disk usage estimates
  Expose disk usage estimates in nodes stats
  S3 repository: Deprecate specifying credentials through env vars, sys props, and remove profile files (elastic#22567)
  Fix Eclipse project generation
  Fix deprecation logging for lenient booleans
  Remove @Header we no longer need
  Make lexer abstract
  [Docs] Remove outdated info about enabling/disabling doc_values (elastic#22694)
  Move lexer hacks to EnhancedPainlessLexer
  Fix incorrect args order passed to createAggregator
  Improve painless's javadocs
  Add TestWithDependenciesPlugin to build (elastic#22646)
  Add parsing from xContent to SearchProfileShardResults and nested classes (elastic#22649)
  Add unit tests for FiltersAggregator (elastic#22678)
  Don't register search response listener in transport clients
  unmute FieldStatsIntegrationIT.testGeoPointNotIndexed, fix already pushed
  Mute FieldStatsIntegrationIT.testGeoPointNotIndexed, for now
  Painless: Add augmentation to string for base 64 (elastic#22665)
  Fix NPE on FieldStats with mixed cluster on version pre/post 5.2 (elastic#22688)
  Add parsing methods for UpdateResponse (elastic#22586)
  ...
cbuescher added a commit to cbuescher/elasticsearch that referenced this pull request Jan 20, 2017
As a follow up to elastic#22649, this changes the resent tests for
parsing parts of search responses to randomly set the humanReadable() flag of
the XContentBuilder that is used to render the responses. This should
help to test that we can parse back thoses classes if the user specifies
`?human=true` in the request url.
cbuescher added a commit that referenced this pull request Jan 24, 2017
As a follow up to #22649, this changes the resent tests for parsing parts of search 
responses to randomly set the humanReadable() flag of the XContentBuilder that 
is used to render the responses. This should help to test that we can parse back 
thoses classes if the user specifies `?human=true` in the request url.
cbuescher added a commit that referenced this pull request Jan 24, 2017
As a follow up to #22649, this changes the resent tests for parsing parts of search
responses to randomly set the humanReadable() flag of the XContentBuilder that
is used to render the responses. This should help to test that we can parse back
thoses classes if the user specifies `?human=true` in the request url.
@cbuescher cbuescher deleted the addParsing-ProfileResult branch November 27, 2025 10:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

>enhancement :Search/Search Search-related issues that do not fall into other categories v5.3.0 v6.0.0-alpha1

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants