diff --git a/src/Elastic.Clients.Elasticsearch/Common/Aggregations/Aggregation.cs b/src/Elastic.Clients.Elasticsearch/Common/Aggregations/Aggregation.cs index 881ed8d2f20..9494b5bc07b 100644 --- a/src/Elastic.Clients.Elasticsearch/Common/Aggregations/Aggregation.cs +++ b/src/Elastic.Clients.Elasticsearch/Common/Aggregations/Aggregation.cs @@ -4,7 +4,8 @@ namespace Elastic.Clients.Elasticsearch.Aggregations; -public interface IAggregation +// This is marked as internal for now, until we are ready to support plugin aggregations. +internal interface IAggregation { string? Name { get; } } diff --git a/src/Elastic.Clients.Elasticsearch/Serialization/AggregationContainerSerializationHelper.cs b/src/Elastic.Clients.Elasticsearch/Serialization/AggregationContainerSerializationHelper.cs index 29524a65618..089f022c3f1 100644 --- a/src/Elastic.Clients.Elasticsearch/Serialization/AggregationContainerSerializationHelper.cs +++ b/src/Elastic.Clients.Elasticsearch/Serialization/AggregationContainerSerializationHelper.cs @@ -7,7 +7,6 @@ namespace Elastic.Clients.Elasticsearch { - internal static class AggregationContainerSerializationHelper { public static AggregationContainer ReadContainer(ref Utf8JsonReader reader, JsonSerializerOptions options) where T : Aggregation @@ -21,13 +20,12 @@ public static AggregationContainer ReadContainer(ref Utf8JsonReader reader, J public static AggregationContainer ReadContainer(string variantName, ref Utf8JsonReader reader, JsonSerializerOptions options) where T : Aggregation { - var variant = JsonSerializer.Deserialize(ref reader, options); - - //variant.Name = variantName; + var variant = JsonSerializer.Deserialize(ref reader, options); var container = new AggregationContainer(variant); - //reader.Read(); + if (container.Variant is Aggregation agg) + agg.Name = variantName; return container; } diff --git a/src/Elastic.Clients.Elasticsearch/Serialization/IsADictionaryConverter.cs b/src/Elastic.Clients.Elasticsearch/Serialization/IsADictionaryConverter.cs index 7540ac4ae0f..777747fc8d4 100644 --- a/src/Elastic.Clients.Elasticsearch/Serialization/IsADictionaryConverter.cs +++ b/src/Elastic.Clients.Elasticsearch/Serialization/IsADictionaryConverter.cs @@ -53,7 +53,6 @@ public override void Write(Utf8JsonWriter writer, TType value, JsonSerializerOpt } } - internal interface IUnionVerifiable { bool IsSuccessful { get; } diff --git a/src/Elastic.Clients.Elasticsearch/Types/Aggregations/AggregationContainer.cs b/src/Elastic.Clients.Elasticsearch/Types/Aggregations/AggregationContainer.cs index 43074c11351..ef213b194ac 100644 --- a/src/Elastic.Clients.Elasticsearch/Types/Aggregations/AggregationContainer.cs +++ b/src/Elastic.Clients.Elasticsearch/Types/Aggregations/AggregationContainer.cs @@ -30,34 +30,7 @@ public partial class AggregationContainer JsonSerializer.Serialize(writer, descriptor, options); }; - public static implicit operator AggregationContainer(Aggregation aggregator) - { - if (aggregator == null) - return null; - - // TODO: Reimplement this fully - as neccesary! - - var container = new AggregationContainer(aggregator) - { - //Meta = aggregator.Meta - }; - - //aggregator.WrapInContainer(container); - - //var bucket = aggregator as BucketAggregationBase; - - //container.Aggregations = bucket?.Aggregations; - - var combinator = aggregator as AggregationCombinator; - if (combinator?.Aggregations != null) - { - var dict = new AggregationDictionary(); - // foreach (var agg in combinator.Aggregations) - // dict.Add(((IAggregation)agg).Name, agg); - // container.Aggregations = dict; - } - - return container; - } + public static implicit operator AggregationContainer(Aggregation aggregator) => + aggregator == null ? null : new AggregationContainer(aggregator); } } diff --git a/src/Elastic.Clients.Elasticsearch/_Generated/Types/Aggregations/AggregationContainer.g.cs b/src/Elastic.Clients.Elasticsearch/_Generated/Types/Aggregations/AggregationContainer.g.cs index 9462eadf24f..21792c16bf0 100644 --- a/src/Elastic.Clients.Elasticsearch/_Generated/Types/Aggregations/AggregationContainer.g.cs +++ b/src/Elastic.Clients.Elasticsearch/_Generated/Types/Aggregations/AggregationContainer.g.cs @@ -27,7 +27,7 @@ namespace Elastic.Clients.Elasticsearch.Aggregations [JsonConverter(typeof(AggregationContainerConverter))] public sealed partial class AggregationContainer { - public AggregationContainer(IAggregation variant) => Variant = variant ?? throw new ArgumentNullException(nameof(variant)); + internal AggregationContainer(IAggregation variant) => Variant = variant ?? throw new ArgumentNullException(nameof(variant)); internal IAggregation Variant { get; } } diff --git a/src/Elastic.Clients.Elasticsearch/_Generated/Types/IndexManagement/MappingLimitSettings.g.cs b/src/Elastic.Clients.Elasticsearch/_Generated/Types/IndexManagement/MappingLimitSettings.g.cs index 443e37a10c9..85fa9da7536 100644 --- a/src/Elastic.Clients.Elasticsearch/_Generated/Types/IndexManagement/MappingLimitSettings.g.cs +++ b/src/Elastic.Clients.Elasticsearch/_Generated/Types/IndexManagement/MappingLimitSettings.g.cs @@ -26,6 +26,10 @@ namespace Elastic.Clients.Elasticsearch.IndexManagement { public sealed partial class MappingLimitSettings { + [JsonInclude] + [JsonPropertyName("coerce")] + public bool? Coerce { get; set; } + [JsonInclude] [JsonPropertyName("depth")] public Elastic.Clients.Elasticsearch.IndexManagement.MappingLimitSettingsDepth? Depth { get; set; } @@ -62,6 +66,8 @@ public MappingLimitSettingsDescriptor() : base() { } + private bool? CoerceValue { get; set; } + private Elastic.Clients.Elasticsearch.IndexManagement.MappingLimitSettingsDepth? DepthValue { get; set; } private MappingLimitSettingsDepthDescriptor DepthDescriptor { get; set; } @@ -100,6 +106,12 @@ public MappingLimitSettingsDescriptor() : base() private Action TotalFieldsDescriptorAction { get; set; } + public MappingLimitSettingsDescriptor Coerce(bool? coerce = true) + { + CoerceValue = coerce; + return Self; + } + public MappingLimitSettingsDescriptor Depth(Elastic.Clients.Elasticsearch.IndexManagement.MappingLimitSettingsDepth? depth) { DepthDescriptor = null; @@ -253,6 +265,12 @@ public MappingLimitSettingsDescriptor TotalFields(Action @@ -88,9 +92,7 @@ public RolloverConditionsDescriptor() : base() private Elastic.Clients.Elasticsearch.ByteSize? MaxPrimaryShardSizeValue { get; set; } - private Elastic.Clients.Elasticsearch.ByteSize? MaxPrimaryShardSizeBytesValue { get; set; } - - private string? MaxSizeValue { get; set; } + private Elastic.Clients.Elasticsearch.ByteSize? MaxSizeValue { get; set; } private Elastic.Clients.Elasticsearch.ByteSize? MaxSizeBytesValue { get; set; } @@ -100,6 +102,10 @@ public RolloverConditionsDescriptor() : base() private long? MinPrimaryShardDocsValue { get; set; } + private Elastic.Clients.Elasticsearch.ByteSize? MinPrimaryShardSizeValue { get; set; } + + private Elastic.Clients.Elasticsearch.ByteSize? MinSizeValue { get; set; } + public RolloverConditionsDescriptor MaxAge(Elastic.Clients.Elasticsearch.Duration? maxAge) { MaxAgeValue = maxAge; @@ -130,13 +136,7 @@ public RolloverConditionsDescriptor MaxPrimaryShardSize(Elastic.Clients.Elastics return Self; } - public RolloverConditionsDescriptor MaxPrimaryShardSizeBytes(Elastic.Clients.Elasticsearch.ByteSize? maxPrimaryShardSizeBytes) - { - MaxPrimaryShardSizeBytesValue = maxPrimaryShardSizeBytes; - return Self; - } - - public RolloverConditionsDescriptor MaxSize(string? maxSize) + public RolloverConditionsDescriptor MaxSize(Elastic.Clients.Elasticsearch.ByteSize? maxSize) { MaxSizeValue = maxSize; return Self; @@ -166,6 +166,18 @@ public RolloverConditionsDescriptor MinPrimaryShardDocs(long? minPrimaryShardDoc return Self; } + public RolloverConditionsDescriptor MinPrimaryShardSize(Elastic.Clients.Elasticsearch.ByteSize? minPrimaryShardSize) + { + MinPrimaryShardSizeValue = minPrimaryShardSize; + return Self; + } + + public RolloverConditionsDescriptor MinSize(Elastic.Clients.Elasticsearch.ByteSize? minSize) + { + MinSizeValue = minSize; + return Self; + } + protected override void Serialize(Utf8JsonWriter writer, JsonSerializerOptions options, IElasticsearchClientSettings settings) { writer.WriteStartObject(); @@ -199,16 +211,10 @@ protected override void Serialize(Utf8JsonWriter writer, JsonSerializerOptions o JsonSerializer.Serialize(writer, MaxPrimaryShardSizeValue, options); } - if (MaxPrimaryShardSizeBytesValue is not null) - { - writer.WritePropertyName("max_primary_shard_size_bytes"); - JsonSerializer.Serialize(writer, MaxPrimaryShardSizeBytesValue, options); - } - - if (!string.IsNullOrEmpty(MaxSizeValue)) + if (MaxSizeValue is not null) { writer.WritePropertyName("max_size"); - writer.WriteStringValue(MaxSizeValue); + JsonSerializer.Serialize(writer, MaxSizeValue, options); } if (MaxSizeBytesValue is not null) @@ -235,6 +241,18 @@ protected override void Serialize(Utf8JsonWriter writer, JsonSerializerOptions o writer.WriteNumberValue(MinPrimaryShardDocsValue.Value); } + if (MinPrimaryShardSizeValue is not null) + { + writer.WritePropertyName("min_primary_shard_size"); + JsonSerializer.Serialize(writer, MinPrimaryShardSizeValue, options); + } + + if (MinSizeValue is not null) + { + writer.WritePropertyName("min_size"); + JsonSerializer.Serialize(writer, MinSizeValue, options); + } + writer.WriteEndObject(); } } diff --git a/src/Playground/Program.cs b/src/Playground/Program.cs index 1c91f501653..d4cdb1016d5 100644 --- a/src/Playground/Program.cs +++ b/src/Playground/Program.cs @@ -3,12 +3,15 @@ // See the LICENSE file in the project root for more information. using Elastic.Clients.Elasticsearch; +using Elastic.Clients.Elasticsearch.Aggregations; using Elastic.Clients.Elasticsearch.IndexManagement; using Elastic.Transport; using Playground; // const string IndexName = "stock-demo-v1"; +AggregationContainer a = new TermsAggregation("test"); + var settings = new ElasticsearchClientSettings(new InMemoryConnection()) .DefaultIndex("default-index") .DefaultMappingFor(m => m diff --git a/tests/Tests/Serialization/Aggregations/ChildrenAggregateSerializationTests.cs b/tests/Tests/Serialization/Aggregations/ChildrenAggregateSerializationTests.cs index c1933e0e98d..3d345b8dd20 100644 --- a/tests/Tests/Serialization/Aggregations/ChildrenAggregateSerializationTests.cs +++ b/tests/Tests/Serialization/Aggregations/ChildrenAggregateSerializationTests.cs @@ -4,9 +4,11 @@ using System.Linq; using Elastic.Clients.Elasticsearch.Experimental; +using VerifyXunit; namespace Tests.Serialization; +[UsesVerify] public class ChildrenAggregateSerializationTests : SerializerTestBase { [U] diff --git a/tests/Tests/Serialization/Aggregations/WritingAggregationsTests.cs b/tests/Tests/Serialization/Aggregations/WritingAggregationsTests.cs new file mode 100644 index 00000000000..976b30324b3 --- /dev/null +++ b/tests/Tests/Serialization/Aggregations/WritingAggregationsTests.cs @@ -0,0 +1,77 @@ +// 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.Threading.Tasks; +using Elastic.Clients.Elasticsearch.Aggregations; +using Tests.Domain; +using VerifyXunit; + +namespace Tests.Serialization; + +[UsesVerify] +public class WritingAggregationsTests : SerializerTestBase +{ + [U] + public async Task CanSerializeAggregationsWrittenInVariousWays_WhichIncludeMultipleSubAggregations() + { + // TODO - Test other cases from https://www.elastic.co/guide/en/elasticsearch/client/net-api/7.17/writing-aggregations.html#writing-aggregations + + // ** First test that the descriptor produces the expected JSON. + + var descriptor = new AggregationContainerDescriptor(aggs => aggs + .Children("name_of_child_agg", child => child + .Type("commits") + .Aggregations(childAggs => childAggs + .Avg("average_per_child", avg => avg.Field(p => p.ConfidenceFactor)) + .Max("max_per_child", max => max.Field(p => p.ConfidenceFactor)) + .Min("min_per_child", min => min.Field(p => p.ConfidenceFactor)) + ))); + + var json = await SerializeAndGetJsonStringAsync(descriptor); + + await Verifier.VerifyJson(json); + + // ** Then test that the object initializer dictionary approach produces the expected (and same) JSON. + + var aggs = new AggregationDictionary + { + { + "name_of_child_agg", new ChildrenAggregation("name_of_child_agg") + { + Type = "commits", + Aggregations = new AggregationDictionary + { + {"average_per_child", new AverageAggregation("average_per_child", "confidenceFactor")}, + {"max_per_child", new MaxAggregation("max_per_child", "confidenceFactor")}, + {"min_per_child", new MinAggregation("min_per_child", "confidenceFactor")}, + } + } + } + }; + + var objectInitializerJson = await SerializeAndGetJsonStringAsync(aggs); + + json.Should().Be(objectInitializerJson); + + // ** Next test the terser object initializer syntax produces the expected (and same) JSON. + + AggregationDictionary terseAggs = new ChildrenAggregation("name_of_child_agg") // NOTE: Must assign to AggregationDictionary. + { + Type = "commits", + Aggregations = + new AverageAggregation("average_per_child", Infer.Field(p => p.ConfidenceFactor)) + && new MaxAggregation("max_per_child", Infer.Field(p => p.ConfidenceFactor)) + && new MinAggregation("min_per_child", Infer.Field(p => p.ConfidenceFactor)) + }; + + var terseAggsJson = await SerializeAndGetJsonStringAsync(terseAggs); + + json.Should().Be(terseAggsJson); + + // ** Test we can deserialise the JSON as an AggregationDictionary + + // TODO + // var result = DeserializeJsonString(json); + } +} diff --git a/tests/Tests/_VerifySnapshots/WritingAggregationsTests.CanSerializeAggregationsWrittenInVariousWays_WhichIncludeMultipleSubAggregations.verified.txt b/tests/Tests/_VerifySnapshots/WritingAggregationsTests.CanSerializeAggregationsWrittenInVariousWays_WhichIncludeMultipleSubAggregations.verified.txt new file mode 100644 index 00000000000..2c7f8c02c82 --- /dev/null +++ b/tests/Tests/_VerifySnapshots/WritingAggregationsTests.CanSerializeAggregationsWrittenInVariousWays_WhichIncludeMultipleSubAggregations.verified.txt @@ -0,0 +1,24 @@ +{ + name_of_child_agg: { + aggregations: { + average_per_child: { + avg: { + field: confidenceFactor + } + }, + max_per_child: { + max: { + field: confidenceFactor + } + }, + min_per_child: { + min: { + field: confidenceFactor + } + } + }, + children: { + type: commits + } + } +} \ No newline at end of file