Skip to content

Commit

Permalink
Community backports for 8.1.3 (#7793) (#7809)
Browse files Browse the repository at this point in the history
* DoubleWithFractionalPortionConverter and FloatWithFractionalPortionConverter should not fall through to always throw a JSONException for non NETCore builds (#7753)

* Complted buckets JSON converter (#7738)

* Boosted non-exhaustive enum deserialization (#7737)

* Optimized `FieldConverter` (#7736)

* Removed unused JsonIgnore (#7735)

* Fixed the equality contract on Metrics type (#7733)

* No allocations in ResponseItem IsValid prop (#7731)

* Refactoring and tiny behavior fix for Ids (#7730)

---------

Co-authored-by: Florian Bernd <git@flobernd.de>
Co-authored-by: Karl-Johan Sjögren <karl.sjogren@gmail.com>
Co-authored-by: Yoh Deadfall <yoh.deadfall@hotmail.com>
  • Loading branch information
4 people committed Jul 11, 2023
1 parent 1bd2994 commit 8a9f285
Show file tree
Hide file tree
Showing 25 changed files with 156 additions and 100 deletions.
19 changes: 14 additions & 5 deletions src/Elastic.Clients.Elasticsearch/Api/ResponseItem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
// See the LICENSE file in the project root for more information.

using System;

namespace Elastic.Clients.Elasticsearch.Core.Bulk;

public abstract partial class ResponseItem
Expand All @@ -15,12 +17,19 @@ public bool IsValid
if (Error is not null)
return false;

return Operation.ToLowerInvariant() switch
var operation = Operation;

if (operation.Equals("delete", StringComparison.OrdinalIgnoreCase))
return Status is 200 or 404;

if (operation.Equals("create", StringComparison.OrdinalIgnoreCase) ||
operation.Equals("update", StringComparison.OrdinalIgnoreCase) ||
operation.Equals("index", StringComparison.OrdinalIgnoreCase))
{
"delete" => Status == 200 || Status == 404,
"update" or "index" or "create" => Status == 200 || Status == 201,
_ => false,
};
return Status is 200 or 201;
}

return false;
}
}
}
11 changes: 11 additions & 0 deletions src/Elastic.Clients.Elasticsearch/Core/IEnumStruct.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Licensed to Elasticsearch B.V under one or more agreements.
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
// See the LICENSE file in the project root for more information.

namespace Elastic.Clients.Elasticsearch.Core;

internal interface IEnumStruct<TSelf> where TSelf : struct, IEnumStruct<TSelf>
{
// TODO: Can be made static when targeting .NET 7 and higher
TSelf Create(string value);
}
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,6 @@ public Field(PropertyInfo property, double? boost = null, string format = null)
/// </remarks>
public string? Format { get; set; }

[JsonIgnore]
internal bool CachableExpression { get; }

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ namespace Elastic.Clients.Elasticsearch;

internal sealed class FieldConverter : JsonConverter<Field>
{
private static readonly JsonEncodedText FieldProperty = JsonEncodedText.Encode("field");
private static readonly JsonEncodedText FormatProperty = JsonEncodedText.Encode("format");

private IElasticsearchClientSettings _settings;

public override void WriteAsPropertyName(Utf8JsonWriter writer, Field value, JsonSerializerOptions options)
Expand Down Expand Up @@ -48,19 +51,19 @@ private static Field ReadObjectField(ref Utf8JsonReader reader)
{
if (reader.TokenType == JsonTokenType.PropertyName)
{
var propertyName = reader.GetString();
reader.Read();

switch (propertyName)
if (reader.ValueTextEquals(FieldProperty.EncodedUtf8Bytes))
{
reader.Read();
field = reader.GetString();
}
else if (reader.ValueTextEquals(FormatProperty.EncodedUtf8Bytes))
{
reader.Read();
format = reader.GetString();
}
else
{
case "field":
field = reader.GetString();
break;
case "format":
format = reader.GetString();
break;
default:
throw new JsonException("Unexpected property while reading `Field`.");
throw new JsonException($"Unexpected property while reading `{nameof(Field)}`.");
}
}
}
Expand All @@ -70,19 +73,13 @@ private static Field ReadObjectField(ref Utf8JsonReader reader)
return new Field(field, format);
}

throw new JsonException("Unable to read `Field` from JSON.");
throw new JsonException($"Unable to read `{nameof(Field)}` from JSON.");
}

public override void Write(Utf8JsonWriter writer, Field value, JsonSerializerOptions options)
{
InitializeSettings(options);

if (value is null)
{
writer.WriteNullValue();
return;
}

var fieldName = _settings.Inferrer.Field(value);

if (string.IsNullOrEmpty(value.Format))
Expand All @@ -92,10 +89,8 @@ public override void Write(Utf8JsonWriter writer, Field value, JsonSerializerOpt
else
{
writer.WriteStartObject();
writer.WritePropertyName("field");
writer.WriteStringValue(fieldName);
writer.WritePropertyName("format");
writer.WriteStringValue(value.Format);
writer.WriteString(FieldProperty, fieldName);
writer.WriteString(FormatProperty, value.Format);
writer.WriteEndObject();
}
}
Expand Down
42 changes: 1 addition & 41 deletions src/Elastic.Clients.Elasticsearch/Core/Infer/Id/Ids.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text.Json;
using System.Text.Json.Serialization;
using Elastic.Transport;

Expand All @@ -22,7 +21,7 @@ public partial class Ids : IUrlParameter, IEquatable<Ids>

public Ids(IList<Id> ids) => _ids = ids;

public Ids(IEnumerable<string> ids) => _ids = ids?.Select(i => new Id(i)).ToList();
public Ids(IEnumerable<string> ids) => _ids = ids.Select(i => new Id(i)).ToList();

public Ids(string value)
{
Expand Down Expand Up @@ -84,42 +83,3 @@ public override int GetHashCode()

public static bool operator !=(Ids left, Ids right) => !Equals(left, right);
}

internal sealed class IdsConverter : JsonConverter<Ids>
{
public override Ids? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TokenType != JsonTokenType.StartArray)
throw new JsonException($"Unexpected JSON token. Expected {JsonTokenType.StartArray} but read {reader.TokenType}");

var ids = new List<Id>();

while (reader.Read() && reader.TokenType != JsonTokenType.EndArray)
{
var id = JsonSerializer.Deserialize<Id>(ref reader, options);

if (id is not null)
ids.Add(id);
}

return new Ids(ids);
}

public override void Write(Utf8JsonWriter writer, Ids value, JsonSerializerOptions options)
{
if (value is null)
{
writer.WriteNullValue();
return;
}

writer.WriteStartArray();

foreach (var id in value.IdsToSerialize)
{
JsonSerializer.Serialize<Id>(writer, id, options);
}

writer.WriteEndArray();
}
}
49 changes: 49 additions & 0 deletions src/Elastic.Clients.Elasticsearch/Core/Infer/Id/IdsConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Licensed to Elasticsearch B.V under one or more agreements.
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
// See the LICENSE file in the project root for more information.

using System;
using System.Collections.Generic;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace Elastic.Clients.Elasticsearch;

internal sealed class IdsConverter : JsonConverter<Ids>
{
public override Ids? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TokenType != JsonTokenType.StartArray)
throw new JsonException($"Unexpected JSON token. Expected {JsonTokenType.StartArray} but read {reader.TokenType}");

var ids = new List<Id>();

while (reader.Read() && reader.TokenType != JsonTokenType.EndArray)
{
var id = JsonSerializer.Deserialize<Id>(ref reader, options);

if (id is not null)
ids.Add(id);
}

return new Ids(ids);
}

public override void Write(Utf8JsonWriter writer, Ids value, JsonSerializerOptions options)
{
if (value is null)
{
writer.WriteNullValue();
return;
}

writer.WriteStartArray();

foreach (var id in value.IdsToSerialize)
{
JsonSerializer.Serialize<Id>(writer, id, options);
}

writer.WriteEndArray();
}
}
16 changes: 12 additions & 4 deletions src/Elastic.Clients.Elasticsearch/Core/Infer/Metric/Metrics.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

using System;
using System.Collections.Generic;
using System.Linq;
using Elastic.Transport;

namespace Elastic.Clients.Elasticsearch;
Expand Down Expand Up @@ -53,8 +52,8 @@ public bool Equals(Metrics other)
{
if (other is null) return false;

// Equality is true when the metrics names in both instances are equal, regardless of their order in the set.
return Values.OrderBy(t => t).SequenceEqual(other.Values.OrderBy(t => t));
// Equality is true when both instances have the same metric names.
return Values.SetEquals(other.Values);
}

string IUrlParameter.GetString(ITransportConfiguration settings) => GetString();
Expand All @@ -71,7 +70,16 @@ private string GetString()
}

/// <inheritdoc />
public override int GetHashCode() => Values != null ? Values.GetHashCode() : 0;
public override int GetHashCode()
{
// Lifting the minimal target framework to .NET Standard 2.1
// would be the best solution ever due to the HashCode type.
var hashCode = 0;
foreach (var metric in Values)
hashCode = (hashCode * 397) ^ metric.GetHashCode();

return hashCode;
}

public static bool operator ==(Metrics left, Metrics right) => Equals(left, right);
public static bool operator !=(Metrics left, Metrics right) => !Equals(left, right);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ public override void Write(Utf8JsonWriter writer, double value, JsonSerializerOp
if (bytes.Length < utf8bytes.Length)
{
bytes.CopyTo(utf8bytes);
return;
}
}
catch
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,18 @@
// See the LICENSE file in the project root for more information.

using System;
using System.Reflection;
using System.Text.Json;
using System.Text.Json.Serialization;
using Elastic.Clients.Elasticsearch.Core;

namespace Elastic.Clients.Elasticsearch.Serialization;

internal sealed class EnumStructConverter<T> : JsonConverter<T> where T : new()
internal sealed class EnumStructConverter<T> : JsonConverter<T> where T : struct, IEnumStruct<T>
{
public override T? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
var value = reader.GetString();

var instance = (T)Activator.CreateInstance(
typeof(T),
BindingFlags.Instance | BindingFlags.NonPublic,
args: new object[] { value }, // TODO: Perf - Review use of ArrayPool
binder: null,
culture: null)!;
var instance = default(T).Create(value);

return instance;
}
Expand All @@ -30,7 +24,7 @@ public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions
var enumValue = value.ToString();

if (!string.IsNullOrEmpty(enumValue))
writer.WriteStringValue(value.ToString());
writer.WriteStringValue(enumValue);
else
writer.WriteNullValue();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ public override void Write(Utf8JsonWriter writer, float value, JsonSerializerOpt
if (bytes.Length < utf8bytes.Length)
{
bytes.CopyTo(utf8bytes);
return;
}
}
catch
Expand Down
33 changes: 21 additions & 12 deletions src/Elastic.Clients.Elasticsearch/Serialization/UnionConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -122,12 +122,6 @@ private class DerivedUnionConverterInner<TType, TItem1, TItem2> : JsonConverter<
public override void Write(Utf8JsonWriter writer, TType value,
JsonSerializerOptions options)
{
if (value is null)
{
writer.WriteNullValue();
return;
}

if (value.Item1 is not null)
{
JsonSerializer.Serialize(writer, value.Item1, value.Item1.GetType(), options);
Expand Down Expand Up @@ -214,15 +208,30 @@ private class BucketsConverter<TBucket> : JsonConverter<Buckets<TBucket>>
{
public override Buckets<TBucket>? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
// TODO - Read ahead to establish the type - For now, hardcoded for lists
return reader.TokenType switch
{
JsonTokenType.Null => null,
JsonTokenType.StartArray => new(JsonSerializer.Deserialize<IReadOnlyCollection<TBucket>>(ref reader, options)),
JsonTokenType.StartObject => new(JsonSerializer.Deserialize<IReadOnlyDictionary<string, TBucket>>(ref reader, options)),
_ => throw new JsonException("Invalid bucket type")
};
}

var bucketType = typeToConvert.GetGenericArguments()[0];
public override void Write(Utf8JsonWriter writer, Buckets<TBucket> value, JsonSerializerOptions options)
{
if (value.Item1 is { } item1)
{
JsonSerializer.Serialize(writer, item1, options);
return;
}

var item = JsonSerializer.Deserialize(ref reader, typeof(IReadOnlyCollection<TBucket>), options);
if (value.Item2 is { } item2)
{
JsonSerializer.Serialize(writer, item2, options);
return;
}

return (Buckets<TBucket>)Activator.CreateInstance(typeof(Buckets<>).MakeGenericType(bucketType), item);
writer.WriteNullValue();
}

public override void Write(Utf8JsonWriter writer, Buckets<TBucket> value, JsonSerializerOptions options) => throw new NotImplementedException();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
using System.Text.Json.Serialization;
using System.Runtime.Serialization;
using Elastic.Transport;
using Elastic.Clients.Elasticsearch.Core;
using Elastic.Clients.Elasticsearch.Serialization;

namespace Elastic.Clients.Elasticsearch.Aggregations;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
using System.Text.Json.Serialization;
using System.Runtime.Serialization;
using Elastic.Transport;
using Elastic.Clients.Elasticsearch.Core;
using Elastic.Clients.Elasticsearch.Serialization;

namespace Elastic.Clients.Elasticsearch.Analysis;
Expand Down

0 comments on commit 8a9f285

Please sign in to comment.