/* 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; using OpenSearch.Net; using OpenSearch.Net.Utf8Json; namespace OpenSearch.Client { /// /// Contains aggregates that are returned by OpenSearch. In OSC, Aggregation always refers to an aggregation /// request to OpenSearch and an Aggregate describes an aggregation response. /// [JsonFormatter(typeof(AggregateDictionaryFormatter))] public class AggregateDictionary : IsAReadOnlyDictionaryBase { internal static readonly char[] TypedKeysSeparator = { '#' }; [SerializationConstructor] public AggregateDictionary(IReadOnlyDictionary backingDictionary) : base(backingDictionary) { } public static AggregateDictionary Default { get; } = new AggregateDictionary(EmptyReadOnly.Dictionary); protected override string Sanitize(string key) { //typed_keys = true on results in aggregation keys being returned as "#" var tokens = TypedKeyTokens(key); return tokens.Length > 1 ? tokens[1] : tokens[0]; } internal static string[] TypedKeyTokens(string key) { var tokens = key.Split(TypedKeysSeparator, 2, StringSplitOptions.RemoveEmptyEntries); return tokens; } public ValueAggregate Min(string key) => TryGet(key); public ValueAggregate Max(string key) => TryGet(key); public ValueAggregate Sum(string key) => TryGet(key); public ValueAggregate Cardinality(string key) => TryGet(key); public ValueAggregate Average(string key) => TryGet(key); public ValueAggregate ValueCount(string key) => TryGet(key); public ValueAggregate AverageBucket(string key) => TryGet(key); public ValueAggregate Derivative(string key) => TryGet(key); public ValueAggregate SumBucket(string key) => TryGet(key); public ValueAggregate MovingAverage(string key) => TryGet(key); public ValueAggregate CumulativeSum(string key) => TryGet(key); public ValueAggregate BucketScript(string key) => TryGet(key); public ValueAggregate SerialDifferencing(string key) => TryGet(key); public ValueAggregate WeightedAverage(string key) => TryGet(key); public KeyedValueAggregate MaxBucket(string key) => TryGet(key); public KeyedValueAggregate MinBucket(string key) => TryGet(key); public ScriptedMetricAggregate ScriptedMetric(string key) { var valueMetric = TryGet(key); return valueMetric != null ? new ScriptedMetricAggregate(valueMetric.Value) { Meta = valueMetric.Meta } : TryGet(key); } public StatsAggregate Stats(string key) => TryGet(key); public StatsAggregate StatsBucket(string key) => TryGet(key); public ExtendedStatsAggregate ExtendedStats(string key) => TryGet(key); public ExtendedStatsAggregate ExtendedStatsBucket(string key) => TryGet(key); public GeoBoundsAggregate GeoBounds(string key) => TryGet(key); public GeoLineAggregate GeoLine(string key) => TryGet(key); public PercentilesAggregate Percentiles(string key) => TryGet(key); public PercentilesAggregate PercentilesBucket(string key) => TryGet(key); public PercentilesAggregate PercentileRanks(string key) => TryGet(key); public TopHitsAggregate TopHits(string key) => TryGet(key); public FiltersAggregate Filters(string key) { var named = TryGet(key); if (named != null) return named; var anonymous = TryGet(key); return anonymous != null ? new FiltersAggregate { Buckets = anonymous.Items.OfType().ToList(), Meta = anonymous.Meta } : null; } public SingleBucketAggregate Global(string key) => TryGet(key); public SingleBucketAggregate Filter(string key) => TryGet(key); public SingleBucketAggregate Missing(string key) => TryGet(key); public SingleBucketAggregate Nested(string key) => TryGet(key); public ValueAggregate Normalize(string key) => TryGet(key); public SingleBucketAggregate ReverseNested(string key) => TryGet(key); public SingleBucketAggregate Children(string key) => TryGet(key); public SingleBucketAggregate Parent(string key) => TryGet(key); public SingleBucketAggregate Sampler(string key) => TryGet(key); public SingleBucketAggregate DiversifiedSampler(string key) => TryGet(key); public GeoCentroidAggregate GeoCentroid(string key) => TryGet(key); public SignificantTermsAggregate SignificantTerms(string key) { var bucket = TryGet(key); return bucket == null ? null : new SignificantTermsAggregate { BgCount = bucket.BgCount, DocCount = bucket.DocCount, Buckets = GetSignificantTermsBuckets(bucket.Items).ToList(), Meta = bucket.Meta }; } public SignificantTermsAggregate SignificantTerms(string key) => SignificantTerms(key); public SignificantTermsAggregate SignificantText(string key) { var bucket = TryGet(key); return bucket == null ? null : new SignificantTermsAggregate { BgCount = bucket.BgCount, DocCount = bucket.DocCount, Buckets = GetSignificantTermsBuckets(bucket.Items).ToList(), Meta = bucket.Meta }; } public SignificantTermsAggregate SignificantText(string key) => SignificantText(key); public TermsAggregate Terms(string key) { var bucket = TryGet(key); return bucket == null ? null : new TermsAggregate { DocCountErrorUpperBound = bucket.DocCountErrorUpperBound, SumOtherDocCount = bucket.SumOtherDocCount, Buckets = GetKeyedBuckets(bucket.Items).ToList(), Meta = bucket.Meta }; } public TermsAggregate Terms(string key) => Terms(key); public MultiBucketAggregate> Histogram(string key) => GetMultiKeyedBucketAggregate(key); public MultiBucketAggregate> GeoHash(string key) => GetMultiKeyedBucketAggregate(key); public MultiBucketAggregate> GeoTile(string key) => GetMultiKeyedBucketAggregate(key); public MultiBucketAggregate> AdjacencyMatrix(string key) => GetMultiKeyedBucketAggregate(key); public MultiBucketAggregate> RareTerms(string key) { var bucket = TryGet(key); return bucket == null ? null : new MultiBucketAggregate> { Buckets = GetRareTermsBuckets(bucket.Items).ToList(), Meta = bucket.Meta }; } public MultiBucketAggregate> RareTerms(string key) => RareTerms(key); public MultiBucketAggregate Range(string key) => GetMultiBucketAggregate(key); public MultiBucketAggregate DateRange(string key) => GetMultiBucketAggregate(key); public MultiBucketAggregate IpRange(string key) => GetMultiBucketAggregate(key); public MultiBucketAggregate GeoDistance(string key) => GetMultiBucketAggregate(key); public MultiBucketAggregate DateHistogram(string key) => GetMultiBucketAggregate(key); public MultiBucketAggregate VariableWidthHistogram(string key) => GetMultiBucketAggregate(key); public MultiTermsAggregate MultiTerms(string key) => MultiTerms(key); public MultiTermsAggregate MultiTerms(string key) { var bucket = TryGet(key); return bucket == null ? null : new MultiTermsAggregate { DocCountErrorUpperBound = bucket.DocCountErrorUpperBound, SumOtherDocCount = bucket.SumOtherDocCount, Buckets = GetMultiTermsBuckets(bucket.Items).ToList(), Meta = bucket.Meta }; } public AutoDateHistogramAggregate AutoDateHistogram(string key) { var bucket = TryGet(key); if (bucket == null) return null; return new AutoDateHistogramAggregate { Buckets = bucket.Items.OfType().ToList(), Meta = bucket.Meta, AutoInterval = bucket.AutoInterval }; } public CompositeBucketAggregate Composite(string key) { var bucket = TryGet(key); if (bucket == null) return null; return new CompositeBucketAggregate { Buckets = bucket.Items.OfType().ToList(), Meta = bucket.Meta, AfterKey = bucket.AfterKey }; } public MatrixStatsAggregate MatrixStats(string key) => TryGet(key); public ValueAggregate MedianAbsoluteDeviation(string key) => TryGet(key); private TAggregate TryGet(string key) where TAggregate : class, IAggregate => BackingDictionary.TryGetValue(key, out var agg) ? agg as TAggregate : null; private MultiBucketAggregate GetMultiBucketAggregate(string key) where TBucket : IBucket { var bucket = TryGet(key); if (bucket == null) return null; return new MultiBucketAggregate { Buckets = bucket.Items.OfType().ToList(), Meta = bucket.Meta }; } private MultiBucketAggregate> GetMultiKeyedBucketAggregate(string key) { var bucket = TryGet(key); if (bucket == null) return null; return new MultiBucketAggregate> { Buckets = GetKeyedBuckets(bucket.Items).ToList(), Meta = bucket.Meta }; } private IEnumerable> GetKeyedBuckets(IEnumerable items) { var buckets = items.Cast>(); foreach (var bucket in buckets) yield return new KeyedBucket(bucket.BackingDictionary) { Key = GetKeyFromBucketKey(bucket.Key), KeyAsString = bucket.KeyAsString, DocCount = bucket.DocCount, DocCountErrorUpperBound = bucket.DocCountErrorUpperBound }; } private IEnumerable> GetSignificantTermsBuckets(IEnumerable items) { var buckets = items.Cast>(); foreach (var bucket in buckets) yield return new SignificantTermsBucket(bucket.BackingDictionary) { Key = GetKeyFromBucketKey(bucket.Key), BgCount = bucket.BgCount, DocCount = bucket.DocCount, Score = bucket.Score }; } private IEnumerable> GetRareTermsBuckets(IEnumerable items) { var buckets = items.Cast>(); foreach (var bucket in buckets) yield return new RareTermsBucket(bucket.BackingDictionary) { Key = GetKeyFromBucketKey(bucket.Key), DocCount = bucket.DocCount.GetValueOrDefault(0) }; } private static IEnumerable> GetMultiTermsBuckets(IEnumerable items) { var buckets = items.Cast>(); foreach (var bucket in buckets) { var aggregates = new MultiTermsBucket(bucket.BackingDictionary) { DocCount = bucket.DocCount, DocCountErrorUpperBound = bucket.DocCountErrorUpperBound, KeyAsString = bucket.KeyAsString }; if (bucket.Key is List allKeys) aggregates.Key = allKeys.Select(GetKeyFromBucketKey).ToList(); else aggregates.Key = new[] { GetKeyFromBucketKey(bucket.Key) }; yield return aggregates; } } private static TKey GetKeyFromBucketKey(object key) => typeof(TKey).IsEnum ? (TKey)Enum.Parse(typeof(TKey), key.ToString(), true) : (TKey)Convert.ChangeType(key, typeof(TKey)); } }