Skip to content

Commit 23e97f1

Browse files
[Backport master] Fix null-ref exception when track total hits is disabled (#5900)
* Fix null-ref exception when track total hits is disabled (#5898) * Fixup Co-authored-by: Steve Gordon <sgordon@hotmail.co.uk>
1 parent dc529b5 commit 23e97f1

File tree

2 files changed

+61
-20
lines changed

2 files changed

+61
-20
lines changed

src/Nest/Search/Search/SearchResponse.cs

+21-20
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ public interface ISearchResponse<out TDocument> : IResponse where TDocument : cl
2828
ClusterStatistics Clusters { get; }
2929

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

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

120121
public class SearchResponse<TDocument> : ResponseBase, ISearchResponse<TDocument> where TDocument : class
@@ -126,7 +127,7 @@ public class SearchResponse<TDocument> : ResponseBase, ISearchResponse<TDocument
126127
private IReadOnlyCollection<IHit<TDocument>> _hits;
127128

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

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

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

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

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

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

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

175180
/// <inheritdoc />
176-
[DataMember(Name ="_shards")]
181+
[DataMember(Name = "_shards")]
177182
public ShardStatistics Shards { get; internal set; }
178183

179184
/// <inheritdoc />
180-
[DataMember(Name ="suggest")]
185+
[DataMember(Name = "suggest")]
181186
public ISuggestDictionary<TDocument> Suggest { get; internal set; } = SuggestDictionary<TDocument>.Default;
182187

183188
/// <inheritdoc />
184-
[DataMember(Name ="terminated_early")]
189+
[DataMember(Name = "terminated_early")]
185190
public bool TerminatedEarly { get; internal set; }
186191

187192
/// <inheritdoc />
188-
[DataMember(Name ="timed_out")]
193+
[DataMember(Name = "timed_out")]
189194
public bool TimedOut { get; internal set; }
190195

191196
/// <inheritdoc />
192-
[DataMember(Name ="took")]
197+
[DataMember(Name = "took")]
193198
public long Took { get; internal set; }
194199

195200
/// <inheritdoc />
196201
[IgnoreDataMember]
197-
public long Total => HitsMetadata?.Total.Value ?? -1;
198-
199-
/// <inheritdoc />
200-
[DataMember(Name = "pit_id")]
201-
public string PointInTimeId { get; internal set; }
202+
public long Total => HitsMetadata?.Total?.Value ?? -1;
202203
}
203204
}
+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
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.Text;
6+
using Elastic.Elasticsearch.Xunit.XunitPlumbing;
7+
using FluentAssertions;
8+
using Nest;
9+
using Tests.Core.Client;
10+
using Tests.Domain;
11+
12+
namespace Tests.Reproduce
13+
{
14+
public class GitHubIssue5892
15+
{
16+
private static readonly byte[] ResponseBytes = Encoding.UTF8.GetBytes(@"{
17+
""took"": 2,
18+
""timed_out"": false,
19+
""_shards"": {
20+
""total"": 1,
21+
""successful"": 1,
22+
""skipped"": 0,
23+
""failed"": 0
24+
},
25+
""hits"": {
26+
""max_score"": null,
27+
""hits"": []
28+
}
29+
}");
30+
31+
[U] public void SearchResponseTotalShouldNotThrowWhenTrackTotalHitsIsFalse()
32+
{
33+
var client = TestClient.FixedInMemoryClient(ResponseBytes);
34+
35+
var response = client.Search<Project>(s => s.Index("test").MatchAll());
36+
37+
response.Total.Should().Be(-1);
38+
}
39+
}
40+
}

0 commit comments

Comments
 (0)