Skip to content

Commit ec32967

Browse files
committed
Include cluster statistics on search response (#3601)
* Include cluster statistics on search response This commit includes cluster statistics returned on a search response under the key _clusters when a cross cluster search is performed. Closes #3512
1 parent 99f5854 commit ec32967

File tree

5 files changed

+141
-9
lines changed

5 files changed

+141
-9
lines changed

src/Nest/CommonOptions/Hit/ShardStatistics.cs

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,29 @@ namespace Nest
77
[JsonObject]
88
public class ShardStatistics
99
{
10-
[JsonProperty]
10+
[JsonProperty("failed")]
1111
public int Failed { get; internal set; }
1212

1313
[JsonProperty("failures")]
1414
public IReadOnlyCollection<ShardFailure> Failures { get; internal set; } = EmptyReadOnly<ShardFailure>.Collection;
1515

16-
[JsonProperty]
16+
[JsonProperty("successful")]
1717
public int Successful { get; internal set; }
1818

19-
[JsonProperty]
19+
[JsonProperty("total")]
20+
public int Total { get; internal set; }
21+
}
22+
23+
[JsonObject]
24+
public class ClusterStatistics
25+
{
26+
[JsonProperty("skipped")]
27+
public int Skipped { get; internal set; }
28+
29+
[JsonProperty("successful")]
30+
public int Successful { get; internal set; }
31+
32+
[JsonProperty("total")]
2033
public int Total { get; internal set; }
2134
}
2235
}

src/Nest/Search/Search/SearchResponse.cs

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,29 @@
55

66
namespace Nest
77
{
8+
/// <summary>
9+
/// A response to a search request
10+
/// </summary>
11+
/// <typeparam name="T">The document type</typeparam>
812
public interface ISearchResponse<T> : IResponse where T : class
913
{
1014
/// <summary>
1115
/// Gets the collection of aggregations
1216
/// </summary>
1317
AggregateDictionary Aggregations { get; }
1418

19+
/// <inheritdoc cref="Aggregations"/>
1520
[Obsolete("Aggs has been renamed to Aggregations and will be removed in NEST 7.x")]
1621
AggregateDictionary Aggs { get; }
1722

23+
/// <summary>
24+
/// Gets the statistics about the clusters on which the search query was executed.
25+
/// </summary>
26+
/// <remarks>
27+
/// Valid for cross cluster searches and Elasticsearch 6.1.0+
28+
/// </remarks>
29+
ClusterStatistics Clusters { get; }
30+
1831
/// <summary>
1932
/// Gets the documents inside the hits, by deserializing <see cref="IHitMetadata{T}.Source" /> into T.
2033
/// <para>
@@ -70,7 +83,7 @@ public interface ISearchResponse<T> : IResponse where T : class
7083
string ScrollId { get; }
7184

7285
/// <summary>
73-
/// Gets the meta data about the shards on which the search query was executed.
86+
/// Gets the statistics about the shards on which the search query was executed.
7487
/// </summary>
7588
ShardStatistics Shards { get; }
7689

@@ -100,21 +113,26 @@ public interface ISearchResponse<T> : IResponse where T : class
100113
long Total { get; }
101114
}
102115

116+
/// <inheritdoc cref="ISearchResponse{T}"/>
103117
[JsonObject]
104118
public class SearchResponse<T> : ResponseBase, ISearchResponse<T> where T : class
105119
{
106120
private IReadOnlyCollection<T> _documents;
107-
108121
private IReadOnlyCollection<FieldValues> _fields;
109-
110122
private IReadOnlyCollection<IHit<T>> _hits;
111123

124+
/// <inheritdoc />
112125
[JsonProperty("aggregations")]
113126
public AggregateDictionary Aggregations { get; internal set; } = AggregateDictionary.Default;
114127

128+
/// <inheritdoc />
115129
[JsonIgnore]
116130
public AggregateDictionary Aggs => Aggregations;
117131

132+
/// <inheritdoc />
133+
[JsonProperty("_clusters")]
134+
public ClusterStatistics Clusters { get; internal set; }
135+
118136
/// <inheritdoc />
119137
[JsonIgnore]
120138
public IReadOnlyCollection<T> Documents =>
@@ -128,43 +146,52 @@ public class SearchResponse<T> : ResponseBase, ISearchResponse<T> where T : clas
128146
.Select(h => h.Fields)
129147
.ToList());
130148

149+
/// <inheritdoc />
131150
[JsonIgnore]
132151
public IReadOnlyCollection<IHit<T>> Hits =>
133152
_hits ?? (_hits = HitsMetadata?.Hits ?? EmptyReadOnly<IHit<T>>.Collection);
134153

154+
/// <inheritdoc />
135155
[JsonProperty("hits")]
136156
public HitsMetadata<T> HitsMetadata { get; internal set; }
137157

158+
/// <inheritdoc />
138159
[JsonIgnore]
139160
public double MaxScore => HitsMetadata?.MaxScore ?? 0;
140161

162+
/// <inheritdoc />
141163
[JsonProperty("num_reduce_phases")]
142164
public long NumberOfReducePhases { get; internal set; }
143165

166+
/// <inheritdoc />
144167
[JsonProperty("profile")]
145168
public Profile Profile { get; internal set; }
146169

147-
/// <summary>
148-
/// Only set when search type = scan and scroll specified
149-
/// </summary>
170+
/// <inheritdoc />
150171
[JsonProperty("_scroll_id")]
151172
public string ScrollId { get; internal set; }
152173

174+
/// <inheritdoc />
153175
[JsonProperty("_shards")]
154176
public ShardStatistics Shards { get; internal set; }
155177

178+
/// <inheritdoc />
156179
[JsonProperty("suggest")]
157180
public SuggestDictionary<T> Suggest { get; internal set; } = SuggestDictionary<T>.Default;
158181

182+
/// <inheritdoc />
159183
[JsonProperty("terminated_early")]
160184
public bool TerminatedEarly { get; internal set; }
161185

186+
/// <inheritdoc />
162187
[JsonProperty("timed_out")]
163188
public bool TimedOut { get; internal set; }
164189

190+
/// <inheritdoc />
165191
[JsonProperty("took")]
166192
public long Took { get; internal set; }
167193

194+
/// <inheritdoc />
168195
[JsonIgnore]
169196
public long Total => HitsMetadata?.Total ?? 0;
170197
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# IMPORTANT! This file is just a template and provides the default settings
2+
# for the test runner. Do not edit this file in anyway unless you intend
3+
# to change the defaults. To change your settings locally make a copy of
4+
# this file in this directory and rename it to `tests.yaml` (which isn't
5+
# tracked by git).
6+
7+
# mode either u (unit test), i (integration test) or m (mixed mode)
8+
mode: i
9+
# the elasticsearch version that should be started
10+
# Can be a snapshot version of sonatype or "latest" to get the latest snapshot of sonatype
11+
elasticsearch_version: 6.6.2
12+
# cluster filter allows you to only run the integration tests of a particular cluster (cluster suffix not needed)
13+
# cluster_filter:
14+
# whether we want to forcefully reseed on the node, if you are starting the tests with a node already running
15+
#force_reseed: true
16+
# do not spawn nodes as part of the test setup if we find a node is already running
17+
# this is opt in during development in CI we never want to see our tests running against an already running node
18+
test_against_already_running_elasticsearch: true
19+
# elasticsearch_out_after_started: true
20+
21+
#random_source_serializer: true
22+
#random_old_connection: true
23+
#seed: 69819
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
using Tests.Core.ManagedElasticsearch.NodeSeeders;
2+
3+
namespace Tests.Core.ManagedElasticsearch.Clusters
4+
{
5+
public class CrossClusterSearchCluster : ClientTestClusterBase
6+
{
7+
protected override void SeedCluster()
8+
{
9+
new DefaultSeeder(Client).SeedNode();
10+
11+
// persist settings for cross cluster search, when cluster_two is not available
12+
Client.ClusterPutSettings(s => s
13+
.Persistent(d => d
14+
.Add("cluster.remote.cluster_two.seeds", new [] { "127.0.0.1:9399" })
15+
.Add("cluster.remote.cluster_two.skip_unavailable", true)
16+
)
17+
);
18+
}
19+
}
20+
}

src/Tests/Tests/Search/Search/SearchApiTests.cs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -502,4 +502,53 @@ protected override void ExpectResponse(IListTasksResponse response)
502502
}
503503
}
504504
}
505+
506+
[SkipVersion("<6.1.0", "_clusters on response only available in 6.1.0+")]
507+
public class CrossClusterSearchApiTests
508+
: ApiIntegrationTestBase<CrossClusterSearchCluster, ISearchResponse<Project>, ISearchRequest, SearchDescriptor<Project>, SearchRequest<Project>>
509+
{
510+
public CrossClusterSearchApiTests(CrossClusterSearchCluster cluster, EndpointUsage usage) : base(cluster, usage) { }
511+
512+
protected override bool ExpectIsValid => true;
513+
514+
protected override object ExpectJson => new
515+
{
516+
query = new
517+
{
518+
match_all = new { }
519+
}
520+
};
521+
522+
protected override int ExpectStatusCode => 200;
523+
524+
protected override Func<SearchDescriptor<Project>, ISearchRequest> Fluent => s => s
525+
.Index(Nest.Indices.Index<Project>().And("cluster_two:project"))
526+
.Query(q => q
527+
.MatchAll()
528+
);
529+
530+
protected override HttpMethod HttpMethod => HttpMethod.POST;
531+
532+
protected override SearchRequest<Project> Initializer => new SearchRequest<Project>(Nest.Indices.Index<Project>().And("cluster_two:project"))
533+
{
534+
Query = new QueryContainer(new MatchAllQuery())
535+
};
536+
537+
protected override string UrlPath => $"/project%2Ccluster_two%3Aproject/doc/_search";
538+
539+
protected override LazyResponses ClientUsage() => Calls(
540+
(c, f) => c.Search(f),
541+
(c, f) => c.SearchAsync(f),
542+
(c, r) => c.Search<Project>(r),
543+
(c, r) => c.SearchAsync<Project>(r)
544+
);
545+
546+
protected override void ExpectResponse(ISearchResponse<Project> response)
547+
{
548+
response.Clusters.Should().NotBeNull();
549+
response.Clusters.Total.Should().Be(2);
550+
response.Clusters.Skipped.Should().Be(1);
551+
response.Clusters.Successful.Should().Be(1);
552+
}
553+
}
505554
}

0 commit comments

Comments
 (0)