/* SPDX-License-Identifier: Apache-2.0 * * The OpenSearch Contributors require contributions made to * this file be licensed under the Apache-2.0 license or a * compatible open source license. */ /* * Modifications Copyright OpenSearch Contributors. See * GitHub history for details. * * Licensed to Elasticsearch B.V. under one or more contributor * license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright * ownership. Elasticsearch B.V. licenses this file to you under * the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ using System; using System.Collections.Generic; using System.Linq.Expressions; using System.Runtime.Serialization; using OpenSearch.Net.Utf8Json; using OpenSearch.Net.Utf8Json.Internal; using OpenSearch.Net.Utf8Json.Resolvers; namespace OpenSearch.Client { /// /// A values source for /// [JsonFormatter(typeof(CompositeAggregationSourceFormatter))] public interface ICompositeAggregationSource { /// /// The field from which to extract value /// [DataMember(Name = "field")] Field Field { get; set; } /// /// By default documents without a value for a given source are ignored. It is possible to include /// them in the response as null by setting this to true /// [DataMember(Name = "missing_bucket")] bool? MissingBucket { get; set; } /// /// The name of the source /// [IgnoreDataMember] string Name { get; set; } /// /// Defines the direction of sorting for each /// value source. Defaults to /// [DataMember(Name = "order")] SortOrder? Order { get; set; } /// /// The type of the source /// [IgnoreDataMember] string SourceType { get; } } /// public abstract class CompositeAggregationSourceBase : ICompositeAggregationSource { internal CompositeAggregationSourceBase() { } protected CompositeAggregationSourceBase(string name) => ((ICompositeAggregationSource)this).Name = name; /// public Field Field { get; set; } /// public bool? MissingBucket { get; set; } /// public SortOrder? Order { get; set; } /// protected abstract string SourceType { get; } /// string ICompositeAggregationSource.Name { get; set; } string ICompositeAggregationSource.SourceType => SourceType; } /// public class CompositeAggregationSourcesDescriptor : DescriptorPromiseBase, IList> where T : class { public CompositeAggregationSourcesDescriptor() : base(new List()) { } /// public CompositeAggregationSourcesDescriptor Terms(string name, Func, ITermsCompositeAggregationSource> selector ) => Assign(selector?.Invoke(new TermsCompositeAggregationSourceDescriptor(name)), (a, v) => a.Add(v)); /// public CompositeAggregationSourcesDescriptor Histogram(string name, Func, IHistogramCompositeAggregationSource> selector ) => Assign(selector?.Invoke(new HistogramCompositeAggregationSourceDescriptor(name)), (a, v) => a.Add(v)); /// public CompositeAggregationSourcesDescriptor DateHistogram(string name, Func, IDateHistogramCompositeAggregationSource> selector ) => Assign(selector?.Invoke(new DateHistogramCompositeAggregationSourceDescriptor(name)), (a, v) => a.Add(v)); /// public CompositeAggregationSourcesDescriptor GeoTileGrid(string name, Func, IGeoTileGridCompositeAggregationSource> selector ) => Assign(selector?.Invoke(new GeoTileGridCompositeAggregationSourceDescriptor(name)), (a, v) => a.Add(v)); } /// public abstract class CompositeAggregationSourceDescriptorBase : DescriptorBase, ICompositeAggregationSource where TDescriptor : CompositeAggregationSourceDescriptorBase, TInterface where TInterface : class, ICompositeAggregationSource { private readonly string _sourceType; protected CompositeAggregationSourceDescriptorBase(string name, string sourceType) { _sourceType = sourceType; Self.Name = name; } Field ICompositeAggregationSource.Field { get; set; } bool? ICompositeAggregationSource.MissingBucket { get; set; } string ICompositeAggregationSource.Name { get; set; } SortOrder? ICompositeAggregationSource.Order { get; set; } string ICompositeAggregationSource.SourceType => _sourceType; /// public TDescriptor Field(Field field) => Assign(field, (a, v) => a.Field = v); /// public TDescriptor Field(Expression> objectPath) => Assign(objectPath, (a, v) => a.Field = v); /// public TDescriptor Order(SortOrder? order) => Assign(order, (a, v) => a.Order = v); /// public TDescriptor MissingBucket(bool? includeMissing = true) => Assign(includeMissing, (a, v) => a.MissingBucket = v); } internal class CompositeAggregationSourceFormatter : IJsonFormatter { public void Serialize(ref JsonWriter writer, ICompositeAggregationSource value, IJsonFormatterResolver formatterResolver) { writer.WriteBeginObject(); writer.WritePropertyName(value.Name); writer.WriteBeginObject(); writer.WritePropertyName(value.SourceType); switch (value) { case ITermsCompositeAggregationSource termsCompositeAggregationSource: Serialize(ref writer, termsCompositeAggregationSource, formatterResolver); break; case IDateHistogramCompositeAggregationSource dateHistogramCompositeAggregationSource: Serialize(ref writer, dateHistogramCompositeAggregationSource, formatterResolver); break; case IHistogramCompositeAggregationSource histogramCompositeAggregationSource: Serialize(ref writer, histogramCompositeAggregationSource, formatterResolver); break; case IGeoTileGridCompositeAggregationSource geoTileGridCompositeAggregationSource: Serialize(ref writer, geoTileGridCompositeAggregationSource, formatterResolver); break; default: Serialize(ref writer, value, formatterResolver); break; } writer.WriteEndObject(); writer.WriteEndObject(); } private static void Serialize(ref JsonWriter writer, TCompositeAggregationSource value, IJsonFormatterResolver formatterResolver ) where TCompositeAggregationSource : ICompositeAggregationSource { var formatter = DynamicObjectResolver.ExcludeNullCamelCase.GetFormatter(); formatter.Serialize(ref writer, value, formatterResolver); } private static readonly AutomataDictionary AggregationSource = new AutomataDictionary { { "terms", 0 }, { "date_histogram", 1 }, { "histogram", 2 }, { "geotile_grid", 3 }, }; public ICompositeAggregationSource Deserialize(ref JsonReader reader, IJsonFormatterResolver formatterResolver) { if (reader.GetCurrentJsonToken() != JsonToken.BeginObject) return null; reader.ReadIsBeginObjectWithVerify(); var name = reader.ReadPropertyName(); reader.ReadIsBeginObjectWithVerify(); // into source var sourcePropertyName = reader.ReadPropertyNameSegmentRaw(); ICompositeAggregationSource compositeAggregationSource = null; if (AggregationSource.TryGetValue(sourcePropertyName, out var value)) { switch (value) { case 0: compositeAggregationSource = formatterResolver.GetFormatter() .Deserialize(ref reader, formatterResolver); break; case 1: compositeAggregationSource = formatterResolver.GetFormatter() .Deserialize(ref reader, formatterResolver); break; case 2: compositeAggregationSource = formatterResolver.GetFormatter() .Deserialize(ref reader, formatterResolver); break; case 3: compositeAggregationSource = formatterResolver.GetFormatter() .Deserialize(ref reader, formatterResolver); break; } } else throw new Exception($"Unknown {nameof(ICompositeAggregationSource)}: {sourcePropertyName.Utf8String()}"); reader.ReadIsEndObjectWithVerify(); reader.ReadIsEndObjectWithVerify(); compositeAggregationSource.Name = name; return compositeAggregationSource; } } }