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

714 lines
16 KiB
C#

/*
* 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;
namespace Tests.Rms.Risk.Mango.Language;
[TestFixture]
public class AstToJsonTests
{
private static readonly JsonSerializerOptions _prettyPrint = new()
{
WriteIndented = true,
TypeInfoResolver = new DefaultJsonTypeInfoResolver()
};
[Test]
public void AddFields()
{
var ast = new AstAggregation("CollectionName");
ast.Add(new AstPipeline([
new AstStageAddFields(
[
new AstLetExpression( new AstExpressionVariable("field1"), "FieldName"),
new AstLetExpression( new AstExpressionVariable("field2")),
]
)
]));
const string expected =
"""
[
{
"$addFields": {
"FieldName": "$field1",
"field2": "$field2"
}
}
]
""";
var result = ast.AsJson()?.ToJsonString(_prettyPrint);
Assert.That( result, Is.EqualTo(expected));
}
[Test]
public void AddFieldsFunctionCall()
{
var ast = new AstAggregation("CollectionName");
var expr = new AstExpressionFunctionCall(
"concat",
[
new AstFunctionArgument(null, new AstExpressionString("many")),
new AstFunctionArgument(null, new AstExpressionString(" miles away"))
]
);
ast.Add(new AstPipeline([
new AstStageAddFields(
[
new AstLetExpression(expr, "Moon")
]
)
]));
const string expected =
"""
[
{
"$addFields": {
"Moon": {
"$concat": [
"many",
" miles away"
]
}
}
}
]
""";
var json = ast.AsJson();
var result = json?.ToJsonString(_prettyPrint);
Assert.That( result, Is.EqualTo(expected));
}
[Test]
public void AddFieldsOperation()
{
var ast = new AstAggregation("CollectionName");
var expr = new AstExpressionOperation(
AstExpressionOperation.OperationType.PLUS,
new AstExpressionVariable("someField"),
new AstExpressionNumber(123)
);
ast.Add(new AstPipeline([
new AstStageAddFields(
[
new AstLetExpression(expr, "Moon")
]
)
]));
const string expected =
"""
[
{
"$addFields": {
"Moon": {
"$add": [
"$someField",
123
]
}
}
}
]
""";
var json = ast.AsJson();
var result = json?.ToJsonString(_prettyPrint);
Assert.That( result, Is.EqualTo(expected));
}
[Test]
public void AddFieldsComplexExpression()
{
var ast = new AstAggregation("CollectionName");
var expr = new AstExpressionFunctionCall(
"concat",
[
new( null, new AstExpressionFunctionCall("str",
[
new( null, new AstExpressionOperation(
AstExpressionOperation.OperationType.MINUS,
new AstExpressionOperation(
AstExpressionOperation.OperationType.PLUS,
new AstExpressionOperation(
AstExpressionOperation.OperationType.DIVIDE,
new AstExpressionVariable("field1"),
new AstExpressionNumber(1.234)
),
new AstExpressionNumber(5)
),
new AstExpressionFunctionCall("abs",
[
new(null,new AstExpressionNumber(-4))
]
)
))
])),
new( null, new AstExpressionString(" miles away"))
]
);
ast.Add(new AstPipeline([
new AstStageAddFields(
[
new AstLetExpression(expr, "Moon")
]
)
]));
const string expected =
"""
[
{
"$addFields": {
"Moon": {
"$concat": [
{
"$str":
{
"$subtract": [
{
"$add": [
{
"$divide": [
"$field1",
1.234
]
},
5
]
},
{
"$abs":
-4
}
]
}
},
" miles away"
]
}
}
}
]
""";
var json = ast.AsJson()?.ToJsonString(_prettyPrint);
var formattedPipeline = JsonNode.Parse(expected)!.ToJsonString(_prettyPrint);
Assert.That( json, Is.EqualTo(formattedPipeline) );
}
[Test]
public void AddFieldsFuncNamedArgs()
{
var ast = new AstAggregation("CollectionName");
var expr = new AstExpressionFunctionCall(
"dateToString",
[
new("format", new AstExpressionString("%Y-%m-%d")),
new("date", new AstExpressionVariable("field1"))
]
);
ast.Add(new AstPipeline([
new AstStageAddFields(
[
new AstLetExpression(expr, "Moon")
]
)
]));
const string expected =
"""
[
{
"$addFields": {
"Moon": {
"$dateToString": {
"format": "%Y-%m-%d",
"date": "$field1"
}
}
}
}
]
""";
var json = ast.AsJson();
var result = json?.ToJsonString(_prettyPrint);
Assert.That( result, Is.EqualTo(expected));
}
[Test]
public void Project()
{
var ast = new AstAggregation("CollectionName");
ast.Add(new AstPipeline([
new AstStageProject(
[],
[
new AstLetExpression( new AstExpressionVariable("_id")),
new AstLetExpression( new AstExpressionVariable("field1"), "FieldName")
]
)
]));
const string expected =
"""
[
{
"$project": {
"_id": 1,
"FieldName": "$field1"
}
}
]
""";
var json = ast.AsJson();
var result = json?.ToJsonString(_prettyPrint);
Assert.That( result, Is.EqualTo(expected));
}
[Test]
public void ProjectWithComplexVarNamed()
{
var ast = new AstAggregation("CollectionName");
ast.Add(new AstPipeline([
new AstStageProject(
[],
[
new AstLetExpression( new AstExpressionVariable("_id")),
new AstLetExpression( new AstExpressionVariable("Very.Complex.Var"), "FieldName")
]
)
]));
const string expected =
"""
[
{
"$project": {
"_id": 1,
"FieldName": "$Very.Complex.Var"
}
}
]
""";
var json = ast.AsJson();
var result = json?.ToJsonString(_prettyPrint);
Assert.That( result, Is.EqualTo(expected));
}
[Test]
public void ProjectWithComplexVarUnnamed()
{
var ast = new AstAggregation("CollectionName");
ast.Add(new AstPipeline([
new AstStageProject(
[],
[
new AstLetExpression( new AstExpressionVariable("_id")),
new AstLetExpression( new AstExpressionVariable("Very.Complex.Var"))
]
)
]));
const string expected =
"""
[
{
"$project": {
"_id": 1,
"Very.Complex.Var": 1
}
}
]
""";
var json = ast.AsJson();
var result = json?.ToJsonString(_prettyPrint);
Assert.That( result, Is.EqualTo(expected));
}
[Test]
public void ProjectExclude()
{
var ast = new AstAggregation("CollectionName");
ast.Add(new AstPipeline([
new AstStageProject(
[],
[
new AstLetExpression( new AstExpressionVariable("field1"))
]
, true
)
]));
const string expected =
"""
[
{
"$project": {
"field1": 0
}
}
]
""";
var json = ast.AsJson();
var result = json?.ToJsonString(_prettyPrint);
Assert.That( result, Is.EqualTo(expected));
}
[Test]
public void WhereTest()
{
var ast = new AstAggregation("CollectionName");
var expr = new AstExpressionOperation(
AstExpressionOperation.OperationType.AND,
new AstExpressionOperation(
AstExpressionOperation.OperationType.NEQ,
new AstExpressionVariable("field1"),
new AstExpressionString("Mars")
),
new AstExpressionOperation(
AstExpressionOperation.OperationType.EQ,
new AstExpressionVariable("field1"),
new AstExpressionString("Moon")
)
);
var whereStage = new AstStageWhere();
whereStage.Add(expr);
ast.Add(new AstPipeline([
whereStage
]));
const string expected =
"""
[
{
"$match": {
"$and": [
{
"field1": {
"$ne": "Mars"
}
},
{
"field1": "Moon"
}
]
}
}
]
""";
var json = ast.AsJson();
var result = json?.ToJsonString(_prettyPrint);
Assert.That( result, Is.EqualTo(expected));
}
[Test]
public void GroupByTest()
{
var ast = new AstAggregation("CollectionName");
var expr = new AstExpressionOperation(
AstExpressionOperation.OperationType.AND,
new AstExpressionOperation(
AstExpressionOperation.OperationType.NEQ,
new AstExpressionVariable("field1"),
new AstExpressionString("Mars")
),
new AstExpressionOperation(
AstExpressionOperation.OperationType.EQ,
new AstExpressionVariable("field1"),
new AstExpressionString("Moon")
)
);
var groupByStage = new AstStageGroupBy(
[
new AstLetExpression(new AstExpressionVariable("idxField1")),
new AstLetExpression(new AstExpressionVariable("idxField2"))
],
[
new AstLetExpression(expr, "IsMoon")
]
);
groupByStage.Add(expr);
ast.Add(new AstPipeline([
groupByStage
]));
const string expected =
"""
[
{
"$group": {
"_id": {
"idxField1": "$idxField1",
"idxField2": "$idxField2"
},
"IsMoon": {
"$and": [
{
"field1": {
"$ne": "Mars"
}
},
{
"field1": "Moon"
}
]
}
}
}
]
""";
var json = ast.AsJson();
var result = json?.ToJsonString(_prettyPrint);
Assert.That( result, Is.EqualTo(expected));
}
[Test]
public void GroupByWithAddToSet()
{
const string source =
"""
[{
"$group": {
"_id": {
"CurvePrefix": "$CurvePrefix",
"CcyPair": "$CcyPair",
"Tenor": "$Tenor"
},
"Order": {
"$max": "$Order"
},
"Items": {
"$addToSet": {
"Name": {
"$concat": [
"$Delta",
" ",
"$Type"
]
},
"Value": "$Value"
}
}
}
}]
""";
var ast = LanguageParser.ParseAggregationJsonToAST("", source);
var json = ast.AsJson();
var result = json?.ToJsonString(_prettyPrint);
var expected = JsonNode.Parse(source)?.ToJsonString(_prettyPrint);
Assert.That( result, Is.EqualTo(expected));
}
[Test]
public void GroupBySingleField()
{
var ast = new AstAggregation("CollectionName");
var groupByStage = new AstStageGroupBy(
[
new AstLetExpression(new AstExpressionVariable("idxField1")),
],
[
]
);
ast.Add(new AstPipeline([
groupByStage
]));
const string expected =
"""
[
{
"$group": {
"_id": "$idxField1"
}
}
]
""";
var json = ast.AsJson();
var result = json?.ToJsonString(_prettyPrint);
Assert.That( result, Is.EqualTo(expected));
}
[Test]
public void GroupByExpression()
{
var ast = new AstAggregation("CollectionName");
var groupByStage = new AstStageGroupBy(
[
new AstLetExpression(new AstExpressionNull()),
],
[
]
);
ast.Add(new AstPipeline([
groupByStage
]));
const string expected =
"""
[
{
"$group": {
"_id": null
}
}
]
""";
var json = ast.AsJson();
var result = json?.ToJsonString(_prettyPrint);
Assert.That( result, Is.EqualTo(expected));
}
[Test]
public void JoinTest()
{
var ast = new AstAggregation("CollectionName");
var expr1 = new AstExpressionOperation(
AstExpressionOperation.OperationType.LTE,
new AstExpressionVariable("distance"),
new AstExpressionNumber(384400.123)
);
var expr2 = new AstExpressionOperation(
AstExpressionOperation.OperationType.NEQ,
new AstExpressionVariable("planet"),
new AstExpressionString("Mars")
);
var joinStage = new AstStageJoin(
"OtherCollection",
"Data",
[
new(new("myField1"), new("otherField1")),
new(new("myField2"), new("otherField2"))
],
[new AstLetExpression(expr1, "IsMoon")],
new(
[
new AstStageWhere(expr2)
]
)
);
ast.Add(new AstPipeline([
joinStage
]));
const string expected =
"""
[
{
"$lookup": {
"from": "OtherCollection",
"localField": "myField1",
"foreignField": "otherField1",
"as": "Data",
"let": {
"IsMoon": {
"distance": {
"$lte": 384400.123
}
}
},
"pipeline": [
{
"$match": {
"planet": {
"$ne": "Mars"
}
}
}
]
}
}
]
""";
var json = ast.AsJson();
var result = json?.ToJsonString(_prettyPrint);
Assert.That( result, Is.EqualTo(expected));
}
[Test]
public void DoTest()
{
var ast = new AstAggregation("CollectionName");
var doStage = new AstStageDo();
doStage.Json = JsonNode.Parse(
"""
{
"$bucket":
{
"groupBy": "$year_born"
}
}
"""
);
ast.Add(new AstPipeline([
doStage
]));
const string expected =
"""
[
{
"$bucket": {
"groupBy": "$year_born"
}
}
]
""";
var json = ast.AsJson();
var result = json?.ToJsonString(_prettyPrint);
Assert.That( result, Is.EqualTo(expected));
}
}