Alexander Shabarshov 2a7a24c9e7 Initial contribution
2025-11-03 14:43:26 +00:00

183 lines
8.0 KiB
C#
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* dbMango
*
* Copyright 2025 Deutsche Bank AG
* SPDX-License-Identifier: Apache-2.0
*
* Licensed 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.Text.Json.Serialization.Metadata;
using Rms.Risk.Mango.Language;
using Rms.Risk.Mango.Language.Ast;
using Rms.Risk.Mango.Pivot.Core;
using Rms.Risk.Mango.Pivot.Core.Models;
namespace Rms.Risk.Mango.Services;
public static class AfhHelpers
{
private static readonly System.Text.Json.JsonSerializerOptions _prettyPrint = new()
{
WriteIndented = true,
TypeInfoResolver = new DefaultJsonTypeInfoResolver()
};
private static Func<PivotDefinition, FilterExpressionTree.ExpressionGroup? /*extra filter*/, Dictionary<string, Type> /*fieldTypes*/, Func<string,string> /*getAggregationOperator*/, string>? _prevConvertToJson;
public static void Init()
{
_prevConvertToJson = PivotDefinition.ConvertToJson;
PivotDefinition.ConvertToJson = ConvertToJson;
}
public static string ConvertToJson(
PivotDefinition pivot,
FilterExpressionTree.ExpressionGroup? extraFilter,
Dictionary<string, Type> fieldTypes,
Func<string, string> getAggregationOperator)
{
if (_prevConvertToJson == null)
throw new ApplicationException("Call AfhHelpers.Init() first");
if ( pivot.PivotType != PivotTypeEnum.AggregationForHumans )
return _prevConvertToJson(pivot, extraFilter, fieldTypes, getAggregationOperator );
var filterText = "";
if ( extraFilter?.IsEmpty == false )
{
var afhFilter = ConvertToExpression(extraFilter!, fieldTypes);
filterText = $" AND ( {afhFilter.AsText()} )";
}
var keys = ConvertKeys(pivot.KeyFields);
var preserveKeys = ConvertKeysPreserve(pivot.KeyFields);
var idToRootKeys = ConvertKeysFromIdToRoot(pivot.KeyFields);
var query = pivot.CustomQuery
.Replace("/*[EXTRA_FILTER]*/", filterText)
.Replace("/*[KEYS]*/", keys)
.Replace("/*[KEYS_PRESERVE]*/", preserveKeys)
.Replace("/*[KEYS_FROM_ID_TO_ROOT]*/", idToRootKeys)
;
var ast = LanguageParser.ParseScriptToAST(query);
var json = ast.AsJson() ?? throw new ApplicationException("Failed to convert AST to JSON");
return json.ToJsonString(_prettyPrint);
}
private static string? ConvertKeys(string[] keys) => string.Join(", ", keys.Select(x => $"'${x}'"));
private static string? ConvertKeysPreserve(string[] keys) => string.Join(", ", keys.Select(x => $"'${x}' AS '${x}'"));
private static string? ConvertKeysFromIdToRoot(string[] keys) => string.Join(", ", keys.Select(x => $"'$_id.{x}' AS '${x}'"));
private static AstExpressionOperation ConvertToExpression(FilterExpressionTree.ExpressionGroup filter, Dictionary<string, Type> fieldTypes)
{
var afhFilter = new AstExpressionOperation(
filter.Condition == FilterExpressionTree.ExpressionGroup.ConditionType.And
? AstExpressionOperation.OperationType.AND
: AstExpressionOperation.OperationType.OR
, filter.Children.Select( x =>
x switch
{
FilterExpressionTree.FieldExpression fe => ConvertToExpression(fe, fieldTypes),
FilterExpressionTree.ExpressionGroup eg => ConvertToExpression(eg, fieldTypes),
_ => throw new NotSupportedException()
})
);
return afhFilter;
}
private static AstExpression ConvertToExpression(FilterExpressionTree.FieldExpression filter, Dictionary<string, Type> fieldTypes)
{
switch (filter.Condition)
{
case FilterExpressionTree.FieldConditionType.EqualTo:
return new AstExpressionOperation(
AstExpressionOperation.OperationType.EQ,
new AstExpressionVariable(filter.Field),
ConvertConstant(filter.Argument, fieldTypes));
case FilterExpressionTree.FieldConditionType.NotEqualTo:
return new AstExpressionOperation(
AstExpressionOperation.OperationType.NEQ,
new AstExpressionVariable(filter.Field),
ConvertConstant(filter.Argument, fieldTypes));
case FilterExpressionTree.FieldConditionType.GreaterThan:
return new AstExpressionOperation(
AstExpressionOperation.OperationType.GT,
new AstExpressionVariable(filter.Field),
ConvertConstant(filter.Argument, fieldTypes));
case FilterExpressionTree.FieldConditionType.LessThan:
return new AstExpressionOperation(
AstExpressionOperation.OperationType.LT,
new AstExpressionVariable(filter.Field),
ConvertConstant(filter.Argument, fieldTypes));
case FilterExpressionTree.FieldConditionType.GreaterThanOrEqualTo:
return new AstExpressionOperation(
AstExpressionOperation.OperationType.GTE,
new AstExpressionVariable(filter.Field),
ConvertConstant(filter.Argument, fieldTypes));
case FilterExpressionTree.FieldConditionType.LessThanOrEqualTo:
return new AstExpressionOperation(
AstExpressionOperation.OperationType.LTE,
new AstExpressionVariable(filter.Field),
ConvertConstant(filter.Argument, fieldTypes));
case FilterExpressionTree.FieldConditionType.IsEmpty:
return new AstExpressionExists(filter.Field, false);
case FilterExpressionTree.FieldConditionType.NotIsEmpty:
return new AstExpressionExists(filter.Field, true);
case FilterExpressionTree.FieldConditionType.IsNull:
return new AstExpressionOperation(
AstExpressionOperation.OperationType.EQ,
new AstExpressionVariable(filter.Field),
new AstExpressionNull());
case FilterExpressionTree.FieldConditionType.NotIsNull:
return new AstExpressionOperation(
AstExpressionOperation.OperationType.NEQ,
new AstExpressionVariable(filter.Field),
new AstExpressionNull());
case FilterExpressionTree.FieldConditionType.Contains:
case FilterExpressionTree.FieldConditionType.StartsWith:
case FilterExpressionTree.FieldConditionType.EndsWith:
case FilterExpressionTree.FieldConditionType.Matches:
case FilterExpressionTree.FieldConditionType.DoesNotMatch:
case FilterExpressionTree.FieldConditionType.DoesNotContain:
case FilterExpressionTree.FieldConditionType.DoesNotStartWith:
case FilterExpressionTree.FieldConditionType.DoesNotEndWith:
default:
throw new NotImplementedException();
}
}
private static AstExpression ConvertConstant(string? filterArgument, Dictionary<string, Type> fieldTypes)
{
if (filterArgument == null)
return new AstExpressionNull();
if ( long.TryParse(filterArgument, out var l))
return new AstExpressionNumber(l);
if ( double.TryParse(filterArgument, out var d))
return new AstExpressionNumber(d);
return new AstExpressionString(filterArgument);
}
}