@page "/user/afh" @page "/user/afh/{DatabaseStr}" @page "/user/afh/{DatabaseStr}/{DatabaseInstanceStr}" @attribute [Authorize] @using Rms.Risk.Mango.Language @using Rms.Risk.Mango.Language.Parsers @inject NavigationManager NavigationManager @inject IUserSession UserSession @inject IJSRuntime JsRuntime @* * 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. *@

Aggregation for humans

@* *@
@code { [Parameter] public string? DatabaseStr { get; set; } [Parameter] public string? DatabaseInstanceStr { get; set; } private string Database { get => UserSession.Database; set { if (UserSession.Database == value) return; UserSession.Database = value; SyncUrl(); } } private string DatabaseInstance { get => UserSession.DatabaseInstance; set { if (UserSession.DatabaseInstance == value) return; UserSession.DatabaseInstance = value; SyncUrl(); } } private string Timeout { get; set; } = "20"; private string FetchSize { get; set; } = "10"; private bool IsReady { get; set; } = true; // private bool ShowAsJson { get; set; } private string ResultActivePage { get; set { if (field == value) return; field = value; InvokeAsync(StateHasChanged); } } = "Table"; private string Text { get; set; } = ""; private string TextJson { get; set; } = ""; private string Result { get; set; } = "{}"; private List ResultBson { get; set; } = []; public ArrayBasedPivotData ResultPivot { get; set; } = new([]); private EditContext? _editContext; private EditContext EditContext => _editContext!; protected override void OnInitialized() { _editContext = new (this); } protected override async Task OnAfterRenderAsync(bool firstRender) { if (!firstRender) return; if (string.IsNullOrWhiteSpace(DatabaseStr)) DatabaseStr = Database; else Database = DatabaseStr; if (string.IsNullOrWhiteSpace(DatabaseInstanceStr)) DatabaseInstanceStr = DatabaseInstance; else DatabaseInstance = DatabaseInstanceStr; SyncUrl(); var text = await JsRuntime.LoadFromLocalStorage("AggregationForHumans"); if (!string.IsNullOrWhiteSpace(text)) { Text = text; await InvokeAsync(StateHasChanged); } } private async Task Format() { await InvokeAsync(StateHasChanged); } private Task Run() => Run(false); private Task Explain() => Run(true); private async Task Run(bool explain) { var cts = new CancellationTokenSource(TimeSpan.FromSeconds(int.Parse(Timeout))); try { IsReady = false; await InvokeAsync(StateHasChanged); var pipelineJson = GetCombinedPipelineJson(); await UpdatePipelineJson(); await JsRuntime.SaveToLocalStorage("AggregationForHumans", Text, cts.Token); UserSession.Collection = GetSelectedCollection(); var res = explain ? await RunExplain(pipelineJson, cts.Token) : await RunAggregate(pipelineJson, cts.Token) ; ResultPivot = res.Item1; ResultBson = res.Item2; await Display(ResultBson); } catch (SyntaxErrorException ex) { var row = ex.Line; var col = ex.Position; try { await JsRuntime.InvokeVoidAsync("DashboardUtils.CodeEditor_SetCaret", CancellationToken.None, row, col); } catch (Exception) { // exception } await Display(ex); } catch (Exception e) { await Display(e); } finally { IsReady = true; await InvokeAsync(StateHasChanged); } } private static readonly System.Text.Json.JsonSerializerOptions _prettyPrint = new () { WriteIndented = true }; private string GetCombinedPipelineJson() { var ast = LanguageParser.ParseScriptToAST(Text); var json = ast.AsJson(); var result = json?.ToJsonString(_prettyPrint) ?? ""; return result; } private Task Display(Exception e) { ResultActivePage = "Json"; Result = e.ToString(); return InvokeAsync(StateHasChanged); } private Task Display(IEnumerable res) { Result = res.ToJson(new() { Indent = true }); return InvokeAsync(StateHasChanged); } private string GetSelectedCollection() { try { var ast = LanguageParser.ParseScriptToAST(Text); return ast.Collection; } catch (Exception) { return ""; } } private async Task<(ArrayBasedPivotData, List)> RunExplain(string json, CancellationToken token) { var service = UserSession.MongoDb; var command = $@"{{ ""aggregate"" : ""{GetSelectedCollection()}"", ""pipeline"" : {json}, ""cursor"" : {{}} }}"; var res = await service.ExplainAsync(command, token); ResultActivePage = "Json"; return (ArrayBasedPivotData.NoData, [res]); } private async Task<(ArrayBasedPivotData, List)> RunAggregate(string text, CancellationToken token) { var service = UserSession.MongoDb; var results = service.AggregateAsyncRaw(text, token: token); var fieldMap = new ConcurrentDictionary { ["find"] = new(false) }; var (pd, list) = await MongoDbDataSource.FetchPivotData( "find", "find", fieldMap, results, null, true, int.Parse(FetchSize), false, token); if (list.Count == 0) list.Add(BsonDocument.Parse(@$"{{""Result"": ""No data fetched from {GetSelectedCollection()}""}}")); return (pd, list); } private void SyncUrl() { var url = NavigationManager.BaseUri + $"user/afh/{Database}"; if (!string.IsNullOrWhiteSpace(DatabaseInstance)) url += $"/{DatabaseInstance}"; JsRuntime.InvokeAsync("DashboardUtils.ChangeUrl", url); } private async Task Copy() { await JsRuntime.InvokeVoidAsync("DashboardUtils.CopyToClipboard", Text); } private async Task Paste() { try { Result = ""; await InvokeAsync(StateHasChanged); var text = await JsRuntime.InvokeAsync("DashboardUtils.PasteFromClipboard"); if (string.IsNullOrWhiteSpace(text)) return; if (text[..Math.Min(text.Length, 100)].Trim().StartsWith("[{")) { try { var ast = LanguageParser.ParseAggregationJsonToAST("", text); text = ast.AsText(); } catch (Exception) { // ignore } } Text = text; await InvokeAsync(StateHasChanged); } catch (Exception e) { await Display(e); } } private async Task OnActivePageChanged(string arg) { if (arg != "Json pipeline") return; await UpdatePipelineJson(); } private async Task UpdatePipelineJson() { try { var json = GetCombinedPipelineJson(); TextJson = json; } catch (Exception e) { await Display(e); } await InvokeAsync(StateHasChanged); } }