Skip to content

Commit dec4e1e

Browse files
authored
Deserialize relation on TotalHits (#3667)
This commit deserializes the relation and renames HitsTotal to TotalHits, line with Java source.
1 parent 778a8f9 commit dec4e1e

File tree

10 files changed

+113
-29
lines changed

10 files changed

+113
-29
lines changed

src/Elasticsearch.Net/Utf8Json/Formatters/EnumFormatter.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ internal class EnumFormatter<T> : IJsonFormatter<T>, IObjectPropertyNameFormatte
158158

159159
static EnumFormatter()
160160
{
161-
var names = new List<String>();
161+
var names = new List<string>();
162162
var values = new List<object>();
163163

164164
var type = typeof(T);

src/Nest/Aggregations/AggregateFormatter.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ private IAggregate GetTopHitsAggregate(ref JsonReader reader, IJsonFormatterReso
205205
{
206206
var count = 0;
207207
double? maxScore = null;
208-
HitsTotal total = null;
208+
TotalHits total = null;
209209
List<LazyDocument> topHits = null;
210210

211211
while (reader.ReadIsInObject(ref count))
@@ -216,7 +216,7 @@ private IAggregate GetTopHitsAggregate(ref JsonReader reader, IJsonFormatterReso
216216
switch (value)
217217
{
218218
case 0:
219-
var hitsFormatter = formatterResolver.GetFormatter<HitsTotal>();
219+
var hitsFormatter = formatterResolver.GetFormatter<TotalHits>();
220220
total = hitsFormatter.Deserialize(ref reader, formatterResolver);
221221
break;
222222
case 1:

src/Nest/Aggregations/Metric/TopHits/TopHitsAggregate.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ internal TopHitsAggregate(IList<LazyDocument> hits, IJsonFormatterResolver forma
1919

2020
public double? MaxScore { get; set; }
2121

22-
public HitsTotal Total { get; set; }
22+
public TotalHits Total { get; set; }
2323

2424
private IEnumerable<Hit<TDocument>> ConvertHits<TDocument>()
2525
where TDocument : class

src/Nest/Search/Search/Hits/HitsMetaData.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@ namespace Nest
55
{
66
public class HitsMetadata<T> where T : class
77
{
8-
[DataMember(Name ="hits")]
8+
[DataMember(Name = "hits")]
99
public IReadOnlyCollection<IHit<T>> Hits { get; internal set; } = EmptyReadOnly<IHit<T>>.Collection;
1010

11-
[DataMember(Name ="max_score")]
11+
[DataMember(Name = "max_score")]
1212
public double? MaxScore { get; internal set; }
1313

14-
[DataMember(Name ="total")]
15-
public HitsTotal Total { get; internal set; }
14+
[DataMember(Name = "total")]
15+
public TotalHits Total { get; internal set; }
1616
}
1717
}

src/Nest/Search/Search/Hits/HitsTotal.cs

Lines changed: 0 additions & 16 deletions
This file was deleted.

src/Nest/Search/Search/Hits/InnerHitsMetaData.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ public class InnerHitsMetadata
1313
public double? MaxScore { get; internal set; }
1414

1515
[DataMember(Name = "total")]
16-
public HitsTotal Total { get; internal set; }
16+
public TotalHits Total { get; internal set; }
1717

1818
public IEnumerable<T> Documents<T>() where T : class
1919
{
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
using System;
2+
using System.Runtime.Serialization;
3+
using Elasticsearch.Net;
4+
5+
namespace Nest
6+
{
7+
/// <summary>
8+
/// Description of the total number of hits of a query. The total hit count
9+
/// can't generally be computed accurately without visiting all matches, which
10+
/// is costly for queries that match lots of documents. Given that it is often
11+
/// enough to have a lower bounds of the number of hits, such as
12+
/// "there are more than 1000 hits", Elasticsearch has options to stop counting as soon
13+
/// as a threshold has been reached in order to improve query times.
14+
/// </summary>
15+
[JsonFormatter(typeof(TotalHitsFormatter))]
16+
public class TotalHits
17+
{
18+
/// <summary>Whether <see cref="Value"/> is the exact hit count, in which case <see cref="Relation"/> is
19+
/// <see cref="TotalHitsRelation.EqualTo"/>, or a lower bound of the total hit count, in which case
20+
/// <see cref="Relation"/> is <see cref="TotalHitsRelation.GreaterThanOrEqualTo"/>
21+
/// </summary>
22+
[DataMember(Name = "relation")]
23+
public TotalHitsRelation? Relation { get; internal set; }
24+
25+
/// <summary>The value of the total hit count. Must be interpreted in the context of <see cref="Relation"/></summary>
26+
[DataMember(Name = "value")]
27+
public long Value { get; internal set; }
28+
}
29+
30+
/// <summary>How the <see cref="TotalHits.Value"/> should be interpreted</summary>
31+
[StringEnum]
32+
public enum TotalHitsRelation
33+
{
34+
/// <summary>The total hit count is equal to <see cref="TotalHits.Value"/></summary>
35+
[EnumMember(Value = "eq")]
36+
EqualTo,
37+
38+
/// <summary>The total hit count is greater than or equal to <see cref="TotalHits.Value"/></summary>
39+
[EnumMember(Value = "gte")]
40+
GreaterThanOrEqualTo,
41+
}
42+
43+
internal class TotalHitsFormatter : IJsonFormatter<TotalHits>
44+
{
45+
private static readonly byte[] ValueField = JsonWriter.GetEncodedPropertyNameWithoutQuotation("value");
46+
private static readonly byte[] RelationField = JsonWriter.GetEncodedPropertyNameWithoutQuotation("relation");
47+
private static readonly EnumFormatter<TotalHitsRelation> RelationFormatter = new EnumFormatter<TotalHitsRelation>(true);
48+
49+
public TotalHits Deserialize(ref JsonReader reader, IJsonFormatterResolver formatterResolver)
50+
{
51+
switch (reader.GetCurrentJsonToken())
52+
{
53+
case JsonToken.BeginObject:
54+
var count = 0;
55+
long value = -1;
56+
TotalHitsRelation? relation = null;
57+
while (reader.ReadIsInObject(ref count))
58+
{
59+
var propertyName = reader.ReadPropertyNameSegmentRaw();
60+
if (propertyName.EqualsBytes(ValueField))
61+
value = reader.ReadInt64();
62+
else if (propertyName.EqualsBytes(RelationField))
63+
relation = RelationFormatter.Deserialize(ref reader, formatterResolver);
64+
else
65+
reader.ReadNextBlock();
66+
}
67+
68+
return new TotalHits { Value = value, Relation = relation };
69+
case JsonToken.Number:
70+
return new TotalHits { Value = reader.ReadInt64() };
71+
default:
72+
reader.ReadNextBlock();
73+
return null;
74+
}
75+
}
76+
77+
public void Serialize(ref JsonWriter writer, TotalHits value, IJsonFormatterResolver formatterResolver)
78+
{
79+
if (value == null)
80+
{
81+
writer.WriteNull();
82+
return;
83+
}
84+
85+
if (value.Relation.HasValue)
86+
{
87+
writer.WriteBeginObject();
88+
writer.WritePropertyName("value");
89+
writer.WriteInt64(value.Value);
90+
writer.WriteValueSeparator();
91+
writer.WritePropertyName("relation");
92+
RelationFormatter.Serialize(ref writer, value.Relation.Value, formatterResolver);
93+
writer.WriteEndObject();
94+
}
95+
else
96+
writer.WriteInt64(value.Value);
97+
}
98+
}
99+
}

src/Nest/Search/Search/SearchResponse.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,6 @@ public class SearchResponse<T> : ResponseBase, ISearchResponse<T> where T : clas
186186

187187
/// <inheritdoc />
188188
[IgnoreDataMember]
189-
public long Total => HitsMetadata?.Total.Value ?? 0;
189+
public long Total => HitsMetadata?.Total.Value ?? -1;
190190
}
191191
}

src/Tests/Tests/Search/MultiSearch/MultiSearchApiTests.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@
1616

1717
namespace Tests.Search.MultiSearch
1818
{
19-
[SkipVersion(">5.0.0-alpha1", "format of percolate query changed.")]
20-
[BlockedByIssue("https://github.com/elastic/elasticsearch/pull/39987")]
2119
public class MultiSearchApiTests
2220
: ApiIntegrationTestBase<ReadOnlyCluster, IMultiSearchResponse, IMultiSearchRequest, MultiSearchDescriptor, MultiSearchRequest>
2321
{

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,10 @@ protected override LazyResponses ClientUsage() => Calls(
9696

9797
protected override void ExpectResponse(ISearchResponse<Project> response)
9898
{
99-
response.Hits.Count().Should().BeGreaterThan(0);
99+
response.Total.Should().BeGreaterThan(0);
100+
response.Hits.Count.Should().BeGreaterThan(0);
101+
response.HitsMetadata.Total.Value.Should().Be(response.Total);
102+
response.HitsMetadata.Total.Relation.Should().Be(TotalHitsRelation.EqualTo);
100103
response.Hits.First().Should().NotBeNull();
101104
response.Hits.First().Source.Should().NotBeNull();
102105
response.Aggregations.Count.Should().BeGreaterThan(0);

0 commit comments

Comments
 (0)