From b9e475e739e82c74a3bd617b8fc4beab767789d4 Mon Sep 17 00:00:00 2001 From: Greg Marzouka Date: Fri, 17 Feb 2017 12:52:18 -0500 Subject: [PATCH] Multiple fixes for the query profiler API - Added time_in_nanos to response (closes #2607) - Respose was missing the aggregations portion - Added full API and serialization tests which we were missing --- src/Nest/Nest.csproj | 2 + .../Search/Profile/AggregationBreakdown.cs | 32 ++++ .../Search/Profile/AggregationProfile.cs | 38 +++++ src/Nest/Search/Search/Profile/Collector.cs | 5 +- .../Search/Search/Profile/QueryProfile.cs | 6 + .../Search/Search/Profile/ShardProfile.cs | 3 + .../Search/Search/SearchProfileApiTests.cs | 140 ++++++++++++++++++ src/Tests/Tests.csproj | 1 + src/Tests/tests.yaml | 2 +- 9 files changed, 227 insertions(+), 2 deletions(-) create mode 100644 src/Nest/Search/Search/Profile/AggregationBreakdown.cs create mode 100644 src/Nest/Search/Search/Profile/AggregationProfile.cs create mode 100644 src/Tests/Search/Search/SearchProfileApiTests.cs diff --git a/src/Nest/Nest.csproj b/src/Nest/Nest.csproj index 63ffa7c9b67..d67ce8f7459 100644 --- a/src/Nest/Nest.csproj +++ b/src/Nest/Nest.csproj @@ -1275,6 +1275,8 @@ + + diff --git a/src/Nest/Search/Search/Profile/AggregationBreakdown.cs b/src/Nest/Search/Search/Profile/AggregationBreakdown.cs new file mode 100644 index 00000000000..e8512cc8cbd --- /dev/null +++ b/src/Nest/Search/Search/Profile/AggregationBreakdown.cs @@ -0,0 +1,32 @@ +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace Nest +{ + public class AggregationBreakdown + { + [JsonProperty("reduce")] + public long Reduce { get; internal set; } + + [JsonProperty("build_aggregation")] + public long BuildAggregation { get; internal set; } + + [JsonProperty("build_aggregation_count")] + public long BuildAggregationCount { get; internal set; } + + [JsonProperty("initialize")] + public long Initialize { get; internal set; } + + [JsonProperty("intialize_count")] + public long InitializeCount { get; internal set; } + + [JsonProperty("reduce_count")] + public long ReduceCount { get; internal set; } + + [JsonProperty("collect")] + public long Collect { get; internal set; } + + [JsonProperty("collect_count")] + public long CollectCount { get; internal set; } + } +} diff --git a/src/Nest/Search/Search/Profile/AggregationProfile.cs b/src/Nest/Search/Search/Profile/AggregationProfile.cs new file mode 100644 index 00000000000..ce5094e043a --- /dev/null +++ b/src/Nest/Search/Search/Profile/AggregationProfile.cs @@ -0,0 +1,38 @@ +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace Nest +{ + public class AggregationProfile + { + /// + /// The Elasticsearch aggregation type + /// + [JsonProperty("type")] + public string Type { get; internal set; } + + /// + /// The user defined name of the aggregation + /// + [JsonProperty("description")] + public string Description { get; internal set; } + + /// + /// The time this aggregation took + /// + [JsonProperty("time")] + public Time Time { get; internal set; } + + /// + /// The time this aggregation took, in nanoseconds + /// + [JsonProperty("time_in_nanos")] + public long TimeInNanoseconds { get; internal set; } + + /// + /// Detailed stats about how the time was spent + /// + [JsonProperty("breakdown")] + public AggregationBreakdown Breakdown { get; internal set; } + } +} diff --git a/src/Nest/Search/Search/Profile/Collector.cs b/src/Nest/Search/Search/Profile/Collector.cs index 9889e3a65ee..daecc987250 100644 --- a/src/Nest/Search/Search/Profile/Collector.cs +++ b/src/Nest/Search/Search/Profile/Collector.cs @@ -14,7 +14,10 @@ public class Collector [JsonProperty("time")] public Time Time { get; internal set; } - [JsonProperty("children")] + [JsonProperty("time_in_nanos")] + public long TimeInNanoseconds { get; internal set; } + + [JsonProperty("children")] public IReadOnlyCollection Children { get; internal set; } = EmptyReadOnly.Collection; diff --git a/src/Nest/Search/Search/Profile/QueryProfile.cs b/src/Nest/Search/Search/Profile/QueryProfile.cs index f0ecf5b7dd1..226fb0d038d 100644 --- a/src/Nest/Search/Search/Profile/QueryProfile.cs +++ b/src/Nest/Search/Search/Profile/QueryProfile.cs @@ -23,6 +23,12 @@ public class QueryProfile [JsonProperty("time")] public Time Time { get; internal set; } + /// + /// The time that this query took in nanoseconds + /// + [JsonProperty("time_in_nanos")] + public long TimeInNanoseconds { get; internal set; } + /// /// Detailed stats about how the time was spent /// diff --git a/src/Nest/Search/Search/Profile/ShardProfile.cs b/src/Nest/Search/Search/Profile/ShardProfile.cs index 5f1d366faaf..5771ca7410d 100644 --- a/src/Nest/Search/Search/Profile/ShardProfile.cs +++ b/src/Nest/Search/Search/Profile/ShardProfile.cs @@ -12,5 +12,8 @@ public class ShardProfile public IReadOnlyCollection Searches { get; internal set; } = EmptyReadOnly.Collection; + [JsonProperty("aggregations")] + public IReadOnlyCollection Aggregations { get; internal set; } = + EmptyReadOnly.Collection; } } diff --git a/src/Tests/Search/Search/SearchProfileApiTests.cs b/src/Tests/Search/Search/SearchProfileApiTests.cs new file mode 100644 index 00000000000..ecad822e7a5 --- /dev/null +++ b/src/Tests/Search/Search/SearchProfileApiTests.cs @@ -0,0 +1,140 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Elasticsearch.Net; +using FluentAssertions; +using Nest; +using Tests.Framework; +using Tests.Framework.Integration; +using Tests.Framework.MockData; + +namespace Tests.Search.Search +{ + public class SearchProfileApiTests : ApiIntegrationTestBase, ISearchRequest, + SearchDescriptor, SearchRequest> + { + public SearchProfileApiTests(ReadOnlyCluster cluster, EndpointUsage usage) : base(cluster, usage) + { + } + + protected override LazyResponses ClientUsage() => Calls( + fluent: (c, f) => c.Search(f), + fluentAsync: (c, f) => c.SearchAsync(f), + request: (c, r) => c.Search(r), + requestAsync: (c, r) => c.SearchAsync(r) + ); + + protected override int ExpectStatusCode => 200; + protected override bool ExpectIsValid => true; + protected override HttpMethod HttpMethod => HttpMethod.POST; + protected override string UrlPath => $"/project/project/_search"; + + protected override object ExpectJson => new + { + profile = true, + query = new + { + match_all = new { } + }, + aggs = new + { + startDates = new + { + terms = new + { + field = "startedOn" + } + } + } + }; + + protected override void ExpectResponse(ISearchResponse response) + { + response.Hits.Count().Should().BeGreaterThan(0); + var profile = response.Profile; + profile.Should().NotBeNull(); + var shardProfiles = profile.Shards; + shardProfiles.Should().NotBeNullOrEmpty(); + foreach (var shardProfile in shardProfiles) + { + shardProfile.Id.Should().NotBeNullOrEmpty(); + var searches = shardProfile.Searches; + searches.Should().NotBeNullOrEmpty(); + foreach (var search in searches) + { + var queries = search.Query; + queries.Should().NotBeNullOrEmpty(); + foreach (var query in queries) + { + query.Should().NotBeNull(); + query.Type.Should().NotBeNullOrEmpty(); + query.Description.Should().NotBeNullOrEmpty(); + query.Time.Should().NotBeNull(); + query.TimeInNanoseconds.Should().BeGreaterThan(0); + query.Breakdown.Should().NotBeNull(); + } + search.RewriteTime.Should().BeGreaterThan(0); + var collectors = search.Collector; + foreach (var collector in collectors) + { + collector.Name.Should().NotBeNullOrEmpty(); + collector.Reason.Should().NotBeNullOrEmpty(); + collector.Time.Should().NotBeNull(); + collector.TimeInNanoseconds.Should().BeGreaterThan(0); + var children = collector.Children; + children.Should().NotBeNull(); + foreach (var child in children) + { + child.Should().NotBeNull(); + child.Name.Should().NotBeNullOrEmpty(); + child.Reason.Should().NotBeNullOrEmpty(); + child.Time.Should().NotBeNull(); + child.TimeInNanoseconds.Should().BeGreaterThan(0); + var grandchildren = child.Children; + grandchildren.Should().NotBeNull(); + foreach (var grandchild in grandchildren) + { + grandchild.Name.Should().NotBeNullOrEmpty(); + grandchild.Reason.Should().NotBeNullOrEmpty(); + grandchild.Time.Should().NotBeNull(); + grandchild.TimeInNanoseconds.Should().BeGreaterThan(0); + } + } + } + } + var aggregations = shardProfile.Aggregations; + aggregations.Should().NotBeNull(); + foreach (var aggregation in aggregations) + { + aggregation.Should().NotBeNull(); + aggregation.Type.Should().NotBeNullOrEmpty(); + aggregation.Description.Should().NotBeNullOrEmpty(); + aggregation.Time.Should().NotBeNull(); + aggregation.TimeInNanoseconds.Should().BeGreaterThan(0); + aggregation.Breakdown.Should().NotBeNull(); + } + } + } + + protected override Func, ISearchRequest> Fluent => s => s + .Profile() + .Query(q => q + .MatchAll() + ) + .Aggregations(aggs => aggs + .Terms("startDates", t => t + .Field(p => p.StartedOn) + ) + ); + + protected override SearchRequest Initializer => new SearchRequest() + { + Profile = true, + Query = new QueryContainer(new MatchAllQuery()), + Aggregations = new TermsAggregation("startDates") + { + Field = "startedOn" + } + }; + } +} diff --git a/src/Tests/Tests.csproj b/src/Tests/Tests.csproj index 7097763b35e..6e7f3510a61 100644 --- a/src/Tests/Tests.csproj +++ b/src/Tests/Tests.csproj @@ -722,6 +722,7 @@ + diff --git a/src/Tests/tests.yaml b/src/Tests/tests.yaml index a0fe32db5de..13a6a91c3bd 100644 --- a/src/Tests/tests.yaml +++ b/src/Tests/tests.yaml @@ -2,7 +2,7 @@ mode: m # the elasticsearch version that should be started # Can be a snapshot version of sonatype or "latest" to get the latest snapshot of sonatype -elasticsearch_version: latest +elasticsearch_version: 5.3.0-SNAPSHOT # cluster filter allows you to only run the integration tests of a particular cluster (cluster suffix not needed) # cluster_filter: # whether we want to forcefully reseed on the node, if you are starting the tests with a node already running