Skip to content

Commit 1d1f7f5

Browse files
authored
Fix null-ref exception when track total hits is disabled (#5898)
1 parent f6f29c5 commit 1d1f7f5

File tree

2 files changed

+64
-20
lines changed

2 files changed

+64
-20
lines changed

src/Nest/Search/Search/SearchResponse.cs

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ public interface ISearchResponse<out TDocument> : IResponse where TDocument : cl
2929
ClusterStatistics Clusters { get; }
3030

3131
/// <summary>
32-
/// Gets the documents inside the hits, by deserializing <see cref="IHitMetadata{T}.Source" /> into <typeparamref name="TDocument" />
32+
/// Gets the documents inside the hits, by deserializing <see cref="IHitMetadata{T}.Source" /> into
33+
/// <typeparamref name="TDocument" />
3334
/// <para>
3435
/// NOTE: if you use <see cref="ISearchRequest.StoredFields" /> on the search request,
3536
/// <see cref="Documents" /> will be empty and you should use <see cref="Fields" />
@@ -69,6 +70,11 @@ public interface ISearchResponse<out TDocument> : IResponse where TDocument : cl
6970
/// </summary>
7071
long NumberOfReducePhases { get; }
7172

73+
/// <summary>
74+
/// When a search is made over a point in time, this will be the ID of the point in time.
75+
/// </summary>
76+
string PointInTimeId { get; }
77+
7278
/// <summary>
7379
/// Gets the results of profiling the search query. Has a value only when
7480
/// <see cref="ISearchRequest.Profile" /> is set to <c>true</c> on the search request.
@@ -111,11 +117,6 @@ public interface ISearchResponse<out TDocument> : IResponse where TDocument : cl
111117
/// Gets the total number of documents matching the search query criteria
112118
/// </summary>
113119
long Total { get; }
114-
115-
/// <summary>
116-
/// When a search is made over a point in time, this will be the ID of the point in time.
117-
/// </summary>
118-
string PointInTimeId { get; }
119120
}
120121

121122
public class SearchResponse<TDocument> : ResponseBase, ISearchResponse<TDocument> where TDocument : class
@@ -127,7 +128,7 @@ public class SearchResponse<TDocument> : ResponseBase, ISearchResponse<TDocument
127128
private IReadOnlyCollection<IHit<TDocument>> _hits;
128129

129130
/// <inheritdoc />
130-
[DataMember(Name ="aggregations")]
131+
[DataMember(Name = "aggregations")]
131132
public AggregateDictionary Aggregations { get; internal set; } = AggregateDictionary.Default;
132133

133134
/// <inheritdoc />
@@ -154,51 +155,51 @@ public class SearchResponse<TDocument> : ResponseBase, ISearchResponse<TDocument
154155
_hits ?? (_hits = HitsMetadata?.Hits ?? EmptyReadOnly<IHit<TDocument>>.Collection);
155156

156157
/// <inheritdoc />
157-
[DataMember(Name ="hits")]
158+
[DataMember(Name = "hits")]
158159
public IHitsMetadata<TDocument> HitsMetadata { get; internal set; }
159160

160161
/// <inheritdoc />
161162
[IgnoreDataMember]
162163
public double MaxScore => HitsMetadata?.MaxScore ?? 0;
163164

164165
/// <inheritdoc />
165-
[DataMember(Name ="num_reduce_phases")]
166+
[DataMember(Name = "num_reduce_phases")]
166167
public long NumberOfReducePhases { get; internal set; }
167168

168169
/// <inheritdoc />
169-
[DataMember(Name ="profile")]
170+
[DataMember(Name = "pit_id")]
171+
public string PointInTimeId { get; internal set; }
172+
173+
/// <inheritdoc />
174+
[DataMember(Name = "profile")]
170175
public Profile Profile { get; internal set; }
171176

172177
/// <inheritdoc />
173178
[DataMember(Name = "_scroll_id")]
174179
public string ScrollId { get; internal set; }
175180

176181
/// <inheritdoc />
177-
[DataMember(Name ="_shards")]
182+
[DataMember(Name = "_shards")]
178183
public ShardStatistics Shards { get; internal set; }
179184

180185
/// <inheritdoc />
181-
[DataMember(Name ="suggest")]
186+
[DataMember(Name = "suggest")]
182187
public ISuggestDictionary<TDocument> Suggest { get; internal set; } = SuggestDictionary<TDocument>.Default;
183188

184189
/// <inheritdoc />
185-
[DataMember(Name ="terminated_early")]
190+
[DataMember(Name = "terminated_early")]
186191
public bool TerminatedEarly { get; internal set; }
187192

188193
/// <inheritdoc />
189-
[DataMember(Name ="timed_out")]
194+
[DataMember(Name = "timed_out")]
190195
public bool TimedOut { get; internal set; }
191196

192197
/// <inheritdoc />
193-
[DataMember(Name ="took")]
198+
[DataMember(Name = "took")]
194199
public long Took { get; internal set; }
195200

196201
/// <inheritdoc />
197202
[IgnoreDataMember]
198-
public long Total => HitsMetadata?.Total.Value ?? -1;
199-
200-
/// <inheritdoc />
201-
[DataMember(Name = "pit_id")]
202-
public string PointInTimeId { get; internal set; }
203+
public long Total => HitsMetadata?.Total?.Value ?? -1;
203204
}
204205
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// Licensed to Elasticsearch B.V under one or more agreements.
2+
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3+
// See the LICENSE file in the project root for more information
4+
5+
using System;
6+
using System.Text;
7+
using Elastic.Elasticsearch.Xunit.XunitPlumbing;
8+
using Elasticsearch.Net;
9+
using FluentAssertions;
10+
using Nest;
11+
using Tests.Domain;
12+
13+
namespace Tests.Reproduce
14+
{
15+
public class GitHubIssue5892
16+
{
17+
private static readonly byte[] ResponseBytes = Encoding.UTF8.GetBytes(@"{
18+
""took"": 2,
19+
""timed_out"": false,
20+
""_shards"": {
21+
""total"": 1,
22+
""successful"": 1,
23+
""skipped"": 0,
24+
""failed"": 0
25+
},
26+
""hits"": {
27+
""max_score"": null,
28+
""hits"": []
29+
}
30+
}");
31+
32+
[U] public void SearchResponseTotalShouldNotThrowWhenTrackTotalHitsIsFalse()
33+
{
34+
var pool = new SingleNodeConnectionPool(new Uri($"http://localhost:9200"));
35+
var settings = new ConnectionSettings(pool, new InMemoryConnection(ResponseBytes));
36+
var client = new ElasticClient(settings);
37+
38+
var response = client.Search<Project>(s => s.Index("test").MatchAll());
39+
40+
response.Total.Should().Be(-1);
41+
}
42+
}
43+
}

0 commit comments

Comments
 (0)