Skip to content

Commit 2855a20

Browse files
authored
Add support for MGet (#6434)
* Generate initial types for mget * Support ResponseItem union deserialization * Add initial tests * Add license headers
1 parent c4df690 commit 2855a20

21 files changed

+1157
-92
lines changed

src/Elastic.Clients.Elasticsearch/Serialization/DefaultRequestResponseSerializer.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ public DefaultRequestResponseSerializer(IElasticsearchClientSettings settings)
5858
new DictionaryConverter(settings),
5959
new PropertyNameConverter(settings),
6060
new IsADictionaryConverter(),
61+
new ResponseItemConverterFactory(),
6162
new UnionConverter()
6263
},
6364
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
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.Json;
7+
using System.Text.Json.Serialization;
8+
9+
namespace Elastic.Clients.Elasticsearch
10+
{
11+
/// <summary>
12+
/// A converter factory able to provide a converter to handle (de)serializing <see cref="ResponseItem{TDocument}"/>.
13+
/// </summary>
14+
internal sealed class ResponseItemConverterFactory : JsonConverterFactory
15+
{
16+
public override bool CanConvert(Type typeToConvert) => typeToConvert.IsGenericType && typeToConvert.GetGenericTypeDefinition() == typeof(ResponseItem<>);
17+
18+
public override JsonConverter? CreateConverter(Type typeToConvert, JsonSerializerOptions options)
19+
{
20+
var documentType = typeToConvert.GetGenericArguments()[0];
21+
22+
return (JsonConverter)Activator.CreateInstance(
23+
typeof(ResponseItemConverter<>).MakeGenericType(documentType));
24+
}
25+
26+
private sealed class ResponseItemConverter<TDocument> : JsonConverter<ResponseItem<TDocument>>
27+
{
28+
public override ResponseItem<TDocument>? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
29+
{
30+
const string exceptionMessage = "Unable to deserialize union.";
31+
var readerCopy = reader;
32+
33+
Exception getResultException = null;
34+
Exception errorException = null;
35+
36+
// TODO - Review and optimise performance, possibly read-ahead to check for the error property and then deserialise
37+
// accordingly is better?
38+
39+
try
40+
{
41+
var result = JsonSerializer.Deserialize<GetResult<TDocument>>(ref readerCopy, options);
42+
43+
// If we have a version number, we can be sure this isn't an error
44+
if (result is not null && result.Version is not null)
45+
{
46+
reader = readerCopy; // Ensure we swap the reader to reflect the data we have consumed.
47+
return new ResponseItem<TDocument>(result);
48+
}
49+
}
50+
catch (Exception ex)
51+
{
52+
getResultException = ex;
53+
}
54+
55+
try
56+
{
57+
var result = JsonSerializer.Deserialize<MultiGetError>(ref reader, options);
58+
59+
if (result is not null && result.Error is not null)
60+
{
61+
return new ResponseItem<TDocument>(result);
62+
}
63+
}
64+
catch (Exception ex)
65+
{
66+
errorException = ex;
67+
}
68+
69+
Exception innerException = null;
70+
71+
if (errorException is not null && getResultException is not null)
72+
{
73+
innerException = new AggregateException(errorException, getResultException);
74+
}
75+
else if (errorException is not null)
76+
{
77+
innerException = errorException;
78+
}
79+
else if (getResultException is not null)
80+
{
81+
innerException = getResultException;
82+
}
83+
84+
if (innerException is not null)
85+
{
86+
throw new JsonException(exceptionMessage, innerException);
87+
}
88+
89+
throw new JsonException(exceptionMessage);
90+
}
91+
92+
// Not implemented as this type is read-only on responses.
93+
public override void Write(Utf8JsonWriter writer, ResponseItem<TDocument> value, JsonSerializerOptions options) =>
94+
throw new NotImplementedException("We never expect to serialize an instance of ResponseItem<TDocument> as its a read-only response type.");
95+
}
96+
}
97+
}

src/Elastic.Clients.Elasticsearch/Serialization/UnionConverter.cs

Lines changed: 78 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
using System;
66
using System.Collections.Generic;
7-
using System.Runtime.Serialization;
87
using System.Text.Json;
98
using System.Text.Json.Serialization;
109
using Elastic.Clients.Elasticsearch.Aggregations;
@@ -13,7 +12,7 @@ namespace Elastic.Clients.Elasticsearch;
1312

1413
internal sealed class UnionConverter : JsonConverterFactory
1514
{
16-
private static readonly HashSet<Type> TypesToSkip = new HashSet<Type>
15+
private static readonly HashSet<Type> TypesToSkip = new()
1716
{
1817
typeof(SourceConfig)
1918
};
@@ -47,11 +46,86 @@ public override JsonConverter CreateConverter(
4746
itemTwoType = type.BaseType.GetGenericArguments()[1];
4847
}
4948

50-
var converter = (JsonConverter)Activator.CreateInstance(typeof(UnionConverterInner<,>).MakeGenericType(itemOneType, itemTwoType));
49+
JsonConverter converter;
50+
51+
if (type.Name == typeof(Union<,>).Name)
52+
{
53+
converter = (JsonConverter)Activator.CreateInstance(typeof(UnionConverterInner<,>).MakeGenericType(itemOneType, itemTwoType));
54+
}
55+
else
56+
{
57+
converter = (JsonConverter)Activator.CreateInstance(typeof(DerivedUnionConverterInner<,,>).MakeGenericType(type, itemOneType, itemTwoType));
58+
}
5159

5260
return converter;
5361
}
5462

63+
private class DerivedUnionConverterInner<TType, TItem1, TItem2> : JsonConverter<TType>
64+
{
65+
public override TType? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
66+
{
67+
// TODO - Aggregate Exception if both fail
68+
69+
var readerCopy = reader;
70+
71+
try
72+
{
73+
var itemOne = JsonSerializer.Deserialize<TItem1>(ref readerCopy, options);
74+
75+
if (itemOne is TItem1)
76+
{
77+
reader = readerCopy;
78+
return (TType)Activator.CreateInstance(typeof(TType), itemOne);
79+
}
80+
}
81+
catch
82+
{
83+
// TODO - Store for aggregate exception
84+
}
85+
86+
try
87+
{
88+
var itemTwo = JsonSerializer.Deserialize<TItem2>(ref reader, options);
89+
90+
if (itemTwo is TItem2)
91+
{
92+
return (TType)Activator.CreateInstance(typeof(TType), itemTwo);
93+
}
94+
}
95+
catch
96+
{
97+
// TODO - Store for aggregate exception
98+
}
99+
100+
throw new JsonException("Unable to deserialize union."); // TODO - Add inner aggregate exception.
101+
}
102+
103+
public override void Write(Utf8JsonWriter writer, TType value,
104+
JsonSerializerOptions options)
105+
{
106+
if (value is null)
107+
{
108+
writer.WriteNullValue();
109+
return;
110+
}
111+
112+
//if (value.Item1 is not null)
113+
//{
114+
// JsonSerializer.Serialize(writer, value.Item1, value.Item1.GetType(), options);
115+
// return;
116+
//}
117+
118+
//if (value.Item2 is not null)
119+
//{
120+
// JsonSerializer.Serialize(writer, value.Item2, value.Item2.GetType(), options);
121+
// return;
122+
//}
123+
124+
throw new JsonException("TODO");
125+
//throw new JsonException("Invalid union type.");
126+
}
127+
}
128+
55129
private class UnionConverterInner<TItem1, TItem2> : JsonConverter<Union<TItem1, TItem2>>
56130
{
57131
public override Union<TItem1, TItem2>? Read(ref Utf8JsonReader reader, Type typeToConvert,
@@ -72,7 +146,7 @@ public override void Write(Utf8JsonWriter writer, Union<TItem1, TItem2> value,
72146
return;
73147
}
74148

75-
throw new SerializationException("Invalid union type");
149+
throw new JsonException("Invalid union type");
76150
}
77151
}
78152

src/Elastic.Clients.Elasticsearch/Types/TermsOrder.cs

Lines changed: 69 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -7,98 +7,97 @@
77
using System.Text.Json;
88
using System.Text.Json.Serialization;
99

10-
namespace Elastic.Clients.Elasticsearch
10+
namespace Elastic.Clients.Elasticsearch;
11+
12+
[JsonConverter(typeof(TermsOrderConverter))]
13+
public readonly struct TermsOrder : IEquatable<TermsOrder>
1114
{
12-
[JsonConverter(typeof(TermsOrderConverter))]
13-
public readonly struct TermsOrder : IEquatable<TermsOrder>
14-
{
15-
public TermsOrder(string key, SortOrder order) => (Key, Order) = (key, order);
15+
public TermsOrder(string key, SortOrder order) => (Key, Order) = (key, order);
1616

17-
public static TermsOrder CountAscending => new() { Key = "_count", Order = SortOrder.Asc };
18-
public static TermsOrder CountDescending => new() { Key = "_count", Order = SortOrder.Desc };
19-
public static TermsOrder KeyAscending => new() { Key = "_key", Order = SortOrder.Asc };
20-
public static TermsOrder KeyDescending => new() { Key = "_key", Order = SortOrder.Desc };
17+
public static TermsOrder CountAscending => new() { Key = "_count", Order = SortOrder.Asc };
18+
public static TermsOrder CountDescending => new() { Key = "_count", Order = SortOrder.Desc };
19+
public static TermsOrder KeyAscending => new() { Key = "_key", Order = SortOrder.Asc };
20+
public static TermsOrder KeyDescending => new() { Key = "_key", Order = SortOrder.Desc };
2121

22-
public string Key { get; init; }
23-
public SortOrder Order { get; init; }
22+
public string Key { get; init; }
23+
public SortOrder Order { get; init; }
2424

25-
public bool Equals(TermsOrder other) => Key == other.Key && Order == other.Order;
26-
public override bool Equals(object obj) => obj is TermsOrder other && Equals(other);
27-
public override int GetHashCode() => (Key, Order).GetHashCode();
28-
public static bool operator ==(TermsOrder lhs, TermsOrder rhs) => lhs.Equals(rhs);
29-
public static bool operator !=(TermsOrder lhs, TermsOrder rhs) => !(lhs == rhs);
30-
}
25+
public bool Equals(TermsOrder other) => Key == other.Key && Order == other.Order;
26+
public override bool Equals(object obj) => obj is TermsOrder other && Equals(other);
27+
public override int GetHashCode() => (Key, Order).GetHashCode();
28+
public static bool operator ==(TermsOrder lhs, TermsOrder rhs) => lhs.Equals(rhs);
29+
public static bool operator !=(TermsOrder lhs, TermsOrder rhs) => !(lhs == rhs);
30+
}
3131

32-
internal sealed class TermsOrderConverter : JsonConverter<TermsOrder>
32+
internal sealed class TermsOrderConverter : JsonConverter<TermsOrder>
33+
{
34+
public override TermsOrder Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
3335
{
34-
public override TermsOrder Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
35-
{
36-
if (reader.TokenType != JsonTokenType.StartObject)
37-
return default;
36+
if (reader.TokenType != JsonTokenType.StartObject)
37+
return default;
3838

39-
reader.Read();
40-
var key = reader.GetString();
39+
reader.Read();
40+
var key = reader.GetString();
4141

42-
reader.Read();
43-
var valueString = reader.GetString();
44-
var value = valueString switch
45-
{
46-
"asc" => SortOrder.Asc,
47-
"desc" => SortOrder.Desc,
48-
_ => throw new JsonException("Unexpected sort order in JSON"),
49-
};
42+
reader.Read();
43+
var valueString = reader.GetString();
44+
var value = valueString switch
45+
{
46+
"asc" => SortOrder.Asc,
47+
"desc" => SortOrder.Desc,
48+
_ => throw new JsonException("Unexpected sort order in JSON"),
49+
};
5050

51-
reader.Read();
51+
reader.Read();
5252

53-
if (reader.TokenType != JsonTokenType.EndObject)
54-
throw new JsonException("JSON did not conform to expected shape");
53+
if (reader.TokenType != JsonTokenType.EndObject)
54+
throw new JsonException("JSON did not conform to expected shape");
5555

56-
return new TermsOrder(key, value);
56+
return new TermsOrder(key, value);
57+
}
58+
59+
public override void Write(Utf8JsonWriter writer, TermsOrder value, JsonSerializerOptions options)
60+
{
61+
if (string.IsNullOrEmpty(value.Key))
62+
{
63+
writer.WriteNullValue();
64+
return;
5765
}
5866

59-
public override void Write(Utf8JsonWriter writer, TermsOrder value, JsonSerializerOptions options)
67+
writer.WriteStartObject();
68+
writer.WritePropertyName(value.Key);
69+
switch (value.Order)
6070
{
61-
if (string.IsNullOrEmpty(value.Key))
62-
{
63-
writer.WriteNullValue();
64-
return;
65-
}
66-
67-
writer.WriteStartObject();
68-
writer.WritePropertyName(value.Key);
69-
switch (value.Order)
70-
{
71-
case SortOrder.Asc:
72-
writer.WriteStringValue("asc");
73-
break;
74-
case SortOrder.Desc:
75-
writer.WriteStringValue("desc");
76-
break;
77-
default:
78-
throw new JsonException("Unknown sort order specified.");
79-
}
80-
writer.WriteEndObject();
71+
case SortOrder.Asc:
72+
writer.WriteStringValue("asc");
73+
break;
74+
case SortOrder.Desc:
75+
writer.WriteStringValue("desc");
76+
break;
77+
default:
78+
throw new JsonException("Unknown sort order specified.");
8179
}
80+
writer.WriteEndObject();
8281
}
82+
}
8383

84-
public sealed class TermsOrderDescriptor : PromiseDescriptor<TermsOrderDescriptor, IList<TermsOrder>>
85-
{
86-
public TermsOrderDescriptor() : base(new List<TermsOrder>()) { }
84+
public sealed class TermsOrderDescriptor : PromiseDescriptor<TermsOrderDescriptor, IList<TermsOrder>>
85+
{
86+
public TermsOrderDescriptor() : base(new List<TermsOrder>()) { }
8787

88-
internal TermsOrderDescriptor(Action<TermsOrderDescriptor> configure) : this() => configure?.Invoke(this);
88+
internal TermsOrderDescriptor(Action<TermsOrderDescriptor> configure) : this() => configure?.Invoke(this);
8989

90-
public TermsOrderDescriptor CountAscending() => Assign(a => a.Add(TermsOrder.CountAscending));
90+
public TermsOrderDescriptor CountAscending() => Assign(a => a.Add(TermsOrder.CountAscending));
9191

92-
public TermsOrderDescriptor CountDescending() => Assign(a => a.Add(TermsOrder.CountDescending));
92+
public TermsOrderDescriptor CountDescending() => Assign(a => a.Add(TermsOrder.CountDescending));
9393

94-
public TermsOrderDescriptor KeyAscending() => Assign(a => a.Add(TermsOrder.KeyAscending));
94+
public TermsOrderDescriptor KeyAscending() => Assign(a => a.Add(TermsOrder.KeyAscending));
9595

96-
public TermsOrderDescriptor KeyDescending() => Assign(a => a.Add(TermsOrder.KeyDescending));
96+
public TermsOrderDescriptor KeyDescending() => Assign(a => a.Add(TermsOrder.KeyDescending));
9797

98-
public TermsOrderDescriptor Ascending(string key) =>
99-
string.IsNullOrWhiteSpace(key) ? this : Assign(key, (a, v) => a.Add(new TermsOrder { Key = v, Order = SortOrder.Asc }));
98+
public TermsOrderDescriptor Ascending(string key) =>
99+
string.IsNullOrWhiteSpace(key) ? this : Assign(key, (a, v) => a.Add(new TermsOrder { Key = v, Order = SortOrder.Asc }));
100100

101-
public TermsOrderDescriptor Descending(string key) =>
102-
string.IsNullOrWhiteSpace(key) ? this : Assign(key, (a, v) => a.Add(new TermsOrder { Key = v, Order = SortOrder.Desc }));
103-
}
101+
public TermsOrderDescriptor Descending(string key) =>
102+
string.IsNullOrWhiteSpace(key) ? this : Assign(key, (a, v) => a.Add(new TermsOrder { Key = v, Order = SortOrder.Desc }));
104103
}

src/Elastic.Clients.Elasticsearch/_Generated/Api/ApiUrlsLookup.g.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,7 @@ internal static class ApiUrlsLookups
215215
internal static ApiUrls MachineLearningUpgradeJobSnapshot = new ApiUrls(new[] { "/_ml/anomaly_detectors/{job_id}/model_snapshots/{snapshot_id}/_upgrade" });
216216
internal static ApiUrls MachineLearningValidateDetector = new ApiUrls(new[] { "/_ml/anomaly_detectors/_validate/detector" });
217217
internal static ApiUrls MachineLearningValidate = new ApiUrls(new[] { "/_ml/anomaly_detectors/_validate" });
218+
internal static ApiUrls NoNamespaceMget = new ApiUrls(new[] { "/_mget", "/{index}/_mget" });
218219
internal static ApiUrls NodesHotThreads = new ApiUrls(new[] { "/_nodes/hot_threads", "/_nodes/{node_id}/hot_threads" });
219220
internal static ApiUrls NodesInfo = new ApiUrls(new[] { "/_nodes", "/_nodes/{node_id}", "/_nodes/{metric}", "/_nodes/{node_id}/{metric}" });
220221
internal static ApiUrls NodesReloadSecureSettings = new ApiUrls(new[] { "/_nodes/reload_secure_settings", "/_nodes/{node_id}/reload_secure_settings" });

0 commit comments

Comments
 (0)