private static FieldAggregationsResult ApplyRules(FieldAggregationsResult result, string[] aggregations) {
            if (result.Aggregations.Count > 10)
                return new FieldAggregationsResult { Message = "Aggregation count exceeded" };

            // Duplicate aggregations
            if (result.Aggregations.Count != aggregations.Length)
                return new FieldAggregationsResult { Message = "Duplicate aggregation detected" };

            // Distinct queries are expensive.
            if (result.Aggregations.Count(a => a.Type == FieldAggregationType.Distinct) > 1)
                return new FieldAggregationsResult { Message = "Distinct aggregation count exceeded" };

            // Term queries are expensive.
            var terms = result.Aggregations.Where(a => a.Type == FieldAggregationType.Term).OfType<TermFieldAggregation>().ToList();
            if (terms.Count > 1
                || terms.Any(a => !_allowedTermFields.Contains(a.Field))
                || terms.Any(a => a.ExcludePattern != null && !_allowedTermExcludesIncludes.Contains(a.ExcludePattern))
                || terms.Any(a => a.IncludePattern != null && !_allowedTermExcludesIncludes.Contains(a.IncludePattern)))
                return new FieldAggregationsResult { Message = "Terms aggregation count exceeded" };

            // Only allow fields that are numeric or have high commonality.
            if (result.Aggregations.Any(a => !_allowedFields.Contains(a.Field)))
                return new FieldAggregationsResult { Message = "Dissallowed field detected" };

            return result;
        }
        private static FieldAggregationsResult ApplyRules(FieldAggregationsResult result, string[] aggregations)
        {
            if (result.Aggregations.Count > 10)
            {
                return new FieldAggregationsResult {
                           Message = "Aggregation count exceeded"
                }
            }
            ;

            // Duplicate aggregations
            if (result.Aggregations.Count != aggregations.Length)
            {
                return new FieldAggregationsResult {
                           Message = "Duplicate aggregation detected"
                }
            }
            ;

            // Distinct queries are expensive.
            if (result.Aggregations.Count(a => a.Type == FieldAggregationType.Distinct) > 1)
            {
                return new FieldAggregationsResult {
                           Message = "Distinct aggregation count exceeded"
                }
            }
            ;

            // Term queries are expensive.
            var terms = result.Aggregations.Where(a => a.Type == FieldAggregationType.Term).OfType <TermFieldAggregation>().ToList();

            if (terms.Count > 1 ||
                terms.Any(a => !_allowedTermFields.Contains(a.Field)) ||
                terms.Any(a => a.ExcludePattern != null && !_allowedTermExcludesIncludes.Contains(a.ExcludePattern)) ||
                terms.Any(a => a.IncludePattern != null && !_allowedTermExcludesIncludes.Contains(a.IncludePattern)))
            {
                return new FieldAggregationsResult {
                           Message = "Terms aggregation count exceeded"
                }
            }
            ;

            // Only allow fields that are numeric or have high commonality.
            if (result.Aggregations.Any(a => !_allowedFields.Contains(a.Field)))
            {
                return new FieldAggregationsResult {
                           Message = "Dissallowed field detected"
                }
            }
            ;

            return(result);
        }
        public static FieldAggregationsResult Process(string query, bool applyRules = true) {
            if (String.IsNullOrEmpty(query))
                return new FieldAggregationsResult { IsValid = true };

            var result = new FieldAggregationsResult { IsValid = true };
            string[] aggregations = query.Split(new [] { ',' }, StringSplitOptions.RemoveEmptyEntries);
            foreach (string aggregation in aggregations) {
                string[] parts = aggregation.Split(new[] { ':' }, StringSplitOptions.RemoveEmptyEntries);
                if (parts.Length < 2 || parts.Length > 3)
                    return new FieldAggregationsResult { Message = $"Invalid aggregation: {aggregation}"};

                string type = parts[0]?.ToLower().Trim();
                string field = parts[1]?.ToLower().Trim();
                if (String.IsNullOrEmpty(type) || String.IsNullOrEmpty(field))
                    return new FieldAggregationsResult { Message = $"Invalid type: {type} or field: {field}" };
                
                if (field.StartsWith("data."))
                    field = $"idx.{field.Substring(5)}-n";
                else if (field.StartsWith("ref."))
                    field = $"idx.{field.Substring(4)}-r";

                var fieldType = GetFieldAggregationTypet(type);
                if (fieldType == null)
                    return new FieldAggregationsResult { Message = $"Invalid type: {type}" };
                
                string defaultValueOrIncludeExclude = parts.Length > 2 && !String.IsNullOrWhiteSpace(parts[2]) ? parts[2]?.Trim() : null;
                if (fieldType == FieldAggregationType.Term) {
                    var term = new TermFieldAggregation { Field = field };
                    if (defaultValueOrIncludeExclude != null) {
                        if (defaultValueOrIncludeExclude.StartsWith("-"))
                            term.ExcludePattern = defaultValueOrIncludeExclude.Substring(1).Trim();
                        else
                            term.IncludePattern = defaultValueOrIncludeExclude;
                    }

                    result.Aggregations.Add(term);
                } else {
                    result.Aggregations.Add(new FieldAggregation {
                        Type = fieldType.Value,
                        Field = field,
                        DefaultValue = ParseDefaultValue(defaultValueOrIncludeExclude)
                    });
                }
            }
            
            if (result.Aggregations.Any(a => !_freeFields.Contains(a.Field)))
                result.UsesPremiumFeatures = true;
            
            return applyRules ? ApplyRules(result, aggregations) : result;
        }
        public static FieldAggregationsResult Process(string query, bool applyRules = true)
        {
            if (String.IsNullOrEmpty(query))
            {
                return new FieldAggregationsResult {
                           IsValid = true
                }
            }
            ;

            var result = new FieldAggregationsResult {
                IsValid = true
            };

            string[] aggregations = query.Split(new [] { ',' }, StringSplitOptions.RemoveEmptyEntries);
            foreach (string aggregation in aggregations)
            {
                string[] parts = aggregation.Split(new[] { ':' }, StringSplitOptions.RemoveEmptyEntries);

                if (parts.Length < 2 || parts.Length > 3)
                {
                    return new FieldAggregationsResult {
                               Message = $"Invalid aggregation: {aggregation}"
                    }
                }
                ;

                string type  = parts[0]?.ToLower().Trim();
                string field = parts[1]?.ToLower().Trim();
                if (String.IsNullOrEmpty(type) || String.IsNullOrEmpty(field))
                {
                    return new FieldAggregationsResult {
                               Message = $"Invalid type: {type} or field: {field}"
                    }
                }
                ;

                if (field.StartsWith("data."))
                {
                    field = $"idx.{field.Substring(5)}-n";
                }
                else if (field.StartsWith("ref."))
                {
                    field = $"idx.{field.Substring(4)}-r";
                }

                var fieldType = GetFieldAggregationTypet(type);
                if (fieldType == null)
                {
                    return new FieldAggregationsResult {
                               Message = $"Invalid type: {type}"
                    }
                }
                ;

                string defaultValueOrIncludeExclude = parts.Length > 2 && !String.IsNullOrWhiteSpace(parts[2]) ? parts[2]?.Trim() : null;
                if (fieldType == FieldAggregationType.Term)
                {
                    var term = new TermFieldAggregation {
                        Field = field
                    };

                    if (defaultValueOrIncludeExclude != null)
                    {
                        if (defaultValueOrIncludeExclude.StartsWith("-"))
                        {
                            term.ExcludePattern = defaultValueOrIncludeExclude.Substring(1).Trim();
                        }
                        else
                        {
                            term.IncludePattern = defaultValueOrIncludeExclude;
                        }
                    }

                    result.Aggregations.Add(term);
                }
                else
                {
                    result.Aggregations.Add(new FieldAggregation {
                        Type         = fieldType.Value,
                        Field        = field,
                        DefaultValue = ParseDefaultValue(defaultValueOrIncludeExclude)
                    });
                }
            }

            if (result.Aggregations.Any(a => !_freeFields.Contains(a.Field)))
            {
                result.UsesPremiumFeatures = true;
            }

            return(applyRules ? ApplyRules(result, aggregations) : result);
        }
        public static FieldAggregationsResult Process(string query, bool applyRules = true)
        {
            if (String.IsNullOrEmpty(query))
            {
                return new FieldAggregationsResult {
                           IsValid = true
                }
            }
            ;

            var result = new FieldAggregationsResult {
                IsValid = true
            };

            string[] aggregations = query.Split(new [] { ',' }, StringSplitOptions.RemoveEmptyEntries);
            foreach (string aggregation in aggregations)
            {
                string[] parts = aggregation.Split(new[] { ':' }, StringSplitOptions.RemoveEmptyEntries);

                if (parts.Length < 2 || parts.Length > 3)
                {
                    return new FieldAggregationsResult {
                               Message = $"Invalid aggregation: {aggregation}"
                    }
                }
                ;

                string type  = parts[0]?.ToLower().Trim();
                string field = parts[1]?.ToLower().Trim();
                if (String.IsNullOrEmpty(type) || String.IsNullOrEmpty(field))
                {
                    return new FieldAggregationsResult {
                               Message = $"Invalid type: {type} or field: {field}"
                    }
                }
                ;

                if (field.StartsWith("data."))
                {
                    field = $"idx.{field.Substring(5)}-n";
                }
                else if (field.StartsWith("ref."))
                {
                    field = $"idx.{field.Substring(4)}-r";
                }

                switch (type)
                {
                case "avg":
                    result.Aggregations.Add(new FieldAggregation {
                        Type = FieldAggregationType.Average, Field = field
                    });
                    break;

                case "distinct":
                    result.Aggregations.Add(new FieldAggregation {
                        Type = FieldAggregationType.Distinct, Field = field
                    });
                    break;

                case "sum":
                    result.Aggregations.Add(new FieldAggregation {
                        Type = FieldAggregationType.Sum, Field = field
                    });
                    break;

                case "min":
                    result.Aggregations.Add(new FieldAggregation {
                        Type = FieldAggregationType.Min, Field = field
                    });
                    break;

                case "max":
                    result.Aggregations.Add(new FieldAggregation {
                        Type = FieldAggregationType.Max, Field = field
                    });
                    break;

                case "last":
                    result.Aggregations.Add(new FieldAggregation {
                        Type = FieldAggregationType.Last, Field = field
                    });
                    break;

                case "term":
                    var term = new TermFieldAggregation {
                        Field = field
                    };
                    if (parts.Length > 2 && !String.IsNullOrWhiteSpace(parts[2]))
                    {
                        if (parts[2].StartsWith("-"))
                        {
                            term.ExcludePattern = parts[2].Substring(1).Trim();
                        }
                        else
                        {
                            term.IncludePattern = parts[2].Trim();
                        }
                    }

                    result.Aggregations.Add(term);
                    break;

                default:
                    return(new FieldAggregationsResult {
                        Message = $"Invalid type: {type} for aggregation: {aggregation}"
                    });
                }
            }

            if (result.Aggregations.Any(a => !_freeFields.Contains(a.Field)))
            {
                result.UsesPremiumFeatures = true;
            }

            if (applyRules)
            {
                if (result.Aggregations.Count > 10)
                {
                    return new FieldAggregationsResult {
                               Message = "Aggregation count exceeded"
                    }
                }
                ;

                // Duplicate aggregations
                if (result.Aggregations.Count != aggregations.Length)
                {
                    return new FieldAggregationsResult {
                               Message = "Duplicate aggregation detected"
                    }
                }
                ;

                // Distinct queries are expensive.
                if (result.Aggregations.Count(a => a.Type == FieldAggregationType.Distinct) > 1)
                {
                    return new FieldAggregationsResult {
                               Message = "Distinct aggregation count exceeded"
                    }
                }
                ;

                // Term queries are expensive.
                var terms = result.Aggregations.Where(a => a.Type == FieldAggregationType.Term).ToList();
                if (terms.Count > 1 || terms.Any(a => !_allowedTermFields.Contains(a.Field)))
                {
                    return new FieldAggregationsResult {
                               Message = "Terms aggregation count exceeded"
                    }
                }
                ;

                // Only allow fields that are numeric or have high commonality.
                if (result.Aggregations.Any(a => !_allowedFields.Contains(a.Field)))
                {
                    return new FieldAggregationsResult {
                               Message = "Dissallowed field detected"
                    }
                }
                ;
            }

            return(result);
        }
    }