From b11abcbf5f2593782d211deb72103cc1cc833eac Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Thu, 13 Mar 2025 11:25:13 +0100 Subject: [PATCH 01/82] Small correction --- .../Reflection/MqlMethod.cs | 2 ++ src/MongoDB.Driver/Mql.cs | 21 +++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/MqlMethod.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/MqlMethod.cs index e04f8d50d13..b32697c7fa4 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/MqlMethod.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/MqlMethod.cs @@ -33,6 +33,7 @@ internal static class MqlMethod private static readonly MethodInfo __field; private static readonly MethodInfo __isMissing; private static readonly MethodInfo __isNullOrMissing; + private static readonly MethodInfo __toBinDataFromString; // static constructor static MqlMethod() @@ -47,6 +48,7 @@ static MqlMethod() __field = ReflectionInfo.Method((object container, string fieldName, IBsonSerializer serializer) => Mql.Field(container, fieldName, serializer)); __isMissing = ReflectionInfo.Method((object field) => Mql.IsMissing(field)); __isNullOrMissing = ReflectionInfo.Method((object field) => Mql.IsNullOrMissing(field)); + __ } // public properties diff --git a/src/MongoDB.Driver/Mql.cs b/src/MongoDB.Driver/Mql.cs index e1ccf7ef4ce..9583b30e392 100644 --- a/src/MongoDB.Driver/Mql.cs +++ b/src/MongoDB.Driver/Mql.cs @@ -152,5 +152,26 @@ public static bool IsNullOrMissing(TField field) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } + + /// + /// + /// + /// + /// + /// + /// + public static BsonBinaryData ToBinData(string field, BsonBinarySubType subtype, ConvertBinDataFormat format) + { + throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); + } + + public enum ConvertBinDataFormat + { + base64, + base64url, + utf8, + hex, + uuid, + } } } From e51d73313b5327e35706e6eddb1e80702f6d8b20 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Thu, 13 Mar 2025 16:07:39 +0100 Subject: [PATCH 02/82] Improvements --- .../Ast/Expressions/AstConvertExpression.cs | 30 ++++- .../Ast/Expressions/AstExpression.cs | 6 +- .../Ast/Visitors/AstNodeVisitor.cs | 3 +- .../Reflection/MqlMethod.cs | 28 ++++- ...essionToAggregationExpressionTranslator.cs | 3 + ...MethodToAggregationExpressionTranslator.cs | 80 ++++++++++++ src/MongoDB.Driver/Mql.cs | 118 ++++++++++++++++++ ...dToAggregationExpressionTranslatorTests.cs | 65 ++++++++++ ...dToAggregationExpressionTranslatorTests.cs | 1 - 9 files changed, 322 insertions(+), 12 deletions(-) create mode 100644 src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs create mode 100644 tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstConvertExpression.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstConvertExpression.cs index 5f78f4a89bd..9a0f19b515d 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstConvertExpression.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstConvertExpression.cs @@ -22,27 +22,35 @@ namespace MongoDB.Driver.Linq.Linq3Implementation.Ast.Expressions internal sealed class AstConvertExpression : AstExpression { private readonly AstExpression _input; + private readonly AstExpression _format; private readonly AstExpression _onError; private readonly AstExpression _onNull; private readonly AstExpression _to; + private readonly AstExpression _subType; public AstConvertExpression( AstExpression input, AstExpression to, AstExpression onError = null, - AstExpression onNull = null) + AstExpression onNull = null, + AstExpression subType = null, + AstExpression format = null) { _input = Ensure.IsNotNull(input, nameof(input)); _to = Ensure.IsNotNull(to, nameof(to)); _onError = onError; _onNull = onNull; + _subType = subType; + _format = format; } public AstExpression Input => _input; + public AstExpression Format => _format; public override AstNodeType NodeType => AstNodeType.ConvertExpression; public AstExpression OnError => _onError; public AstExpression OnNull => _onNull; public AstExpression To => _to; + public AstExpression SubType => _subType; public override AstNode Accept(AstNodeVisitor visitor) { @@ -56,9 +64,16 @@ public override BsonValue Render() { "$convert", new BsonDocument { { "input", _input.Render() }, - { "to", _to.Render() }, + { "to", _to.Render(), _subType == null }, + { "to", () => new BsonDocument + { + {"type", _to.Render() }, + {"subtype", _subType.Render()}, + }, _subType != null + }, { "onError", () => _onError.Render(), _onError != null }, - { "onNull", () => _onNull.Render(), _onNull != null } + { "onNull", () => _onNull.Render(), _onNull != null }, + { "format", () => _format.Render(), _format != null} } } }; @@ -68,14 +83,17 @@ public AstConvertExpression Update( AstExpression input, AstExpression to, AstExpression onError, - AstExpression onNull) + AstExpression onNull, + AstExpression subType, + AstExpression format) { - if (input == _input && to == _to && onError == _onError && onNull == _onNull) + if (input == _input && to == _to && onError == _onError && onNull == _onNull && + subType == _subType && format == _format) { return this; } - return new AstConvertExpression(input, to, onError, onNull); + return new AstConvertExpression(input, to, onError, onNull, subType, format); } } } diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstExpression.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstExpression.cs index eb37ffde294..b49561bb90d 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstExpression.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstExpression.cs @@ -255,14 +255,14 @@ public static AstExpression Constant(BsonValue value) return new AstConstantExpression(value); } - public static AstExpression Convert(AstExpression input, AstExpression to, AstExpression onError = null, AstExpression onNull = null) + public static AstExpression Convert(AstExpression input, AstExpression to, AstExpression onError = null, AstExpression onNull = null, + AstExpression subType = null, AstExpression format = null) { Ensure.IsNotNull(input, nameof(input)); Ensure.IsNotNull(to, nameof(to)); if (to is AstConstantExpression toConstantExpression && - (toConstantExpression.Value as BsonString)?.Value is string toValue && - toValue != null && + (toConstantExpression.Value as BsonString)?.Value is { } toValue && onError == null && onNull == null) { diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Visitors/AstNodeVisitor.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Visitors/AstNodeVisitor.cs index 1222d0060e9..fe8cff3cc16 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Visitors/AstNodeVisitor.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Visitors/AstNodeVisitor.cs @@ -226,7 +226,8 @@ public virtual AstNode VisitConstantExpression(AstConstantExpression node) public virtual AstNode VisitConvertExpression(AstConvertExpression node) { - return node.Update(VisitAndConvert(node.Input), VisitAndConvert(node.To), VisitAndConvert(node.OnError), VisitAndConvert(node.OnNull)); + return node.Update(VisitAndConvert(node.Input), VisitAndConvert(node.To), VisitAndConvert(node.OnError), VisitAndConvert(node.OnNull), + VisitAndConvert(node.SubType), VisitAndConvert(node.Format)); } public virtual AstNode VisitCountStage(AstCountStage node) diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/MqlMethod.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/MqlMethod.cs index b32697c7fa4..d13d22fe31c 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/MqlMethod.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/MqlMethod.cs @@ -34,6 +34,13 @@ internal static class MqlMethod private static readonly MethodInfo __isMissing; private static readonly MethodInfo __isNullOrMissing; private static readonly MethodInfo __toBinDataFromString; + private static readonly MethodInfo __toBinDataFromInt; + private static readonly MethodInfo __toBinDataFromLong; + private static readonly MethodInfo __toBinDataFromDouble; + private static readonly MethodInfo __toBinDataFromStringWithOnErrorAndOnNull; + private static readonly MethodInfo __toBinDataFromIntWithOnErrorAndOnNull; + private static readonly MethodInfo __toBinDataFromLongWithOnErrorAndOnNull; + private static readonly MethodInfo __toBinDataFromDoubleWithOnErrorAndOnNull; // static constructor static MqlMethod() @@ -48,7 +55,18 @@ static MqlMethod() __field = ReflectionInfo.Method((object container, string fieldName, IBsonSerializer serializer) => Mql.Field(container, fieldName, serializer)); __isMissing = ReflectionInfo.Method((object field) => Mql.IsMissing(field)); __isNullOrMissing = ReflectionInfo.Method((object field) => Mql.IsNullOrMissing(field)); - __ + __toBinDataFromString = ReflectionInfo.Method((string field, BsonBinarySubType subType, Mql.ConvertBinDataFormat format) => Mql.ToBinData(field, subType, format)); + __toBinDataFromInt = ReflectionInfo.Method((int field, BsonBinarySubType subType, Mql.ConvertBinDataFormat format) => Mql.ToBinData(field, subType, format)); + __toBinDataFromLong = ReflectionInfo.Method((long field, BsonBinarySubType subType, Mql.ConvertBinDataFormat format) => Mql.ToBinData(field, subType, format)); + __toBinDataFromDouble = ReflectionInfo.Method((double field, BsonBinarySubType subType, Mql.ConvertBinDataFormat format) => Mql.ToBinData(field, subType, format)); + __toBinDataFromStringWithOnErrorAndOnNull = ReflectionInfo.Method((string field, BsonBinarySubType subType, Mql.ConvertBinDataFormat format, string onError, string onNull) + => Mql.ToBinData(field, subType, format, onError, onNull)); + __toBinDataFromIntWithOnErrorAndOnNull = ReflectionInfo.Method((int field, BsonBinarySubType subType, Mql.ConvertBinDataFormat format, int onError, int onNull) + => Mql.ToBinData(field, subType, format, onError, onNull)); + __toBinDataFromLongWithOnErrorAndOnNull = ReflectionInfo.Method((long field, BsonBinarySubType subType, Mql.ConvertBinDataFormat format, long onError, long onNull) + => Mql.ToBinData(field, subType, format, onError, onNull)); + __toBinDataFromDoubleWithOnErrorAndOnNull = ReflectionInfo.Method((double field, BsonBinarySubType subType, Mql.ConvertBinDataFormat format, double onError, double onNull) + => Mql.ToBinData(field, subType, format, onError, onNull)); } // public properties @@ -62,5 +80,13 @@ static MqlMethod() public static MethodInfo Field => __field; public static MethodInfo IsMissing => __isMissing; public static MethodInfo IsNullOrMissing => __isNullOrMissing; + public static MethodInfo ToBinDataFromString => __toBinDataFromString; + public static MethodInfo ToBinDataFromInt => __toBinDataFromInt; + public static MethodInfo ToBinDataFromLong => __toBinDataFromLong; + public static MethodInfo ToBinDataFromDouble => __toBinDataFromDouble; + public static MethodInfo ToBinDataFromStringWithOnErrorAndOnNull => __toBinDataFromStringWithOnErrorAndOnNull; + public static MethodInfo ToBinDataFromIntWithOnErrorAndOnNull => __toBinDataFromIntWithOnErrorAndOnNull; + public static MethodInfo ToBinDataFromLongWithOnErrorAndOnNull => __toBinDataFromLongWithOnErrorAndOnNull; + public static MethodInfo ToBinDataFromDoubleWithOnErrorAndOnNull => __toBinDataFromDoubleWithOnErrorAndOnNull; } } diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodCallExpressionToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodCallExpressionToAggregationExpressionTranslator.cs index 5eb324df3fd..b44804acd0c 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodCallExpressionToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodCallExpressionToAggregationExpressionTranslator.cs @@ -203,6 +203,9 @@ public static TranslatedExpression Translate(TranslationContext context, MethodC case "TrimEnd": case "TrimStart": return TrimMethodToAggregationExpressionTranslator.Translate(context, expression); + + case "ToBinData": + return ConvertMethodToAggregationExpressionTranslator.Translate(context, expression); } throw new ExpressionNotSupportedException(expression); diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs new file mode 100644 index 00000000000..ac12715d7fa --- /dev/null +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs @@ -0,0 +1,80 @@ +/* Copyright 2010-present MongoDB Inc. + * + * 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.Linq.Expressions; +using System.Reflection; +using MongoDB.Bson; +using MongoDB.Bson.Serialization.Serializers; +using MongoDB.Driver.Linq.Linq3Implementation.Ast.Expressions; +using MongoDB.Driver.Linq.Linq3Implementation.Misc; +using MongoDB.Driver.Linq.Linq3Implementation.Reflection; + +namespace MongoDB.Driver.Linq.Linq3Implementation.Translators.ExpressionToAggregationExpressionTranslators.MethodTranslators +{ + internal class ConvertMethodToAggregationExpressionTranslator + { + private static readonly MethodInfo[] __toBinDataMethods = + { + MqlMethod.ToBinDataFromString, + MqlMethod.ToBinDataFromInt, + MqlMethod.ToBinDataFromLong, + MqlMethod.ToBinDataFromDouble + }; + + private static readonly MethodInfo[] __withOnErrorAndOnNullMethods = + { + MqlMethod.ToBinDataFromStringWithOnErrorAndOnNull, + MqlMethod.ToBinDataFromIntWithOnErrorAndOnNull, + MqlMethod.ToBinDataFromLongWithOnErrorAndOnNull, + MqlMethod.ToBinDataFromDoubleWithOnErrorAndOnNull + }; + + public static TranslatedExpression Translate(TranslationContext context, MethodCallExpression expression) + { + var method = expression.Method; + var arguments = expression.Arguments; + + if (!method.IsOneOf(__toBinDataMethods, __withOnErrorAndOnNullMethods)) + { + throw new ExpressionNotSupportedException(expression); + } + + var fieldExpression = arguments[0]; + var fieldTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, fieldExpression); + var fieldAst = fieldTranslation.Ast; + var resultSerializer = BsonBinaryDataSerializer.Instance; + + var subTypeExpression = arguments[1]; + var subTypeTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, subTypeExpression); + var subTypeAst = subTypeTranslation.Ast; + + var formatExpression = arguments[2]; + var formatTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, formatExpression); + var formatAst = formatTranslation.Ast; + + if (method.IsOneOf(__withOnErrorAndOnNullMethods)) + { + var onErrorExpression = arguments[3]; + var onErrorTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, onErrorExpression); + var onErrorAst = onErrorTranslation.Ast; + + //TODO Continue + } + + var ast = AstExpression.Convert(fieldAst, AstExpression.Constant(BsonType.Binary), subType: subTypeAst, format: formatAst); + return new TranslatedExpression(expression, ast, resultSerializer); + } + } +} \ No newline at end of file diff --git a/src/MongoDB.Driver/Mql.cs b/src/MongoDB.Driver/Mql.cs index 9583b30e392..3f9cad6e945 100644 --- a/src/MongoDB.Driver/Mql.cs +++ b/src/MongoDB.Driver/Mql.cs @@ -165,12 +165,130 @@ public static BsonBinaryData ToBinData(string field, BsonBinarySubType subtype, throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public static BsonBinaryData ToBinData(string field, BsonBinarySubType subtype, ConvertBinDataFormat format, + string onError, string onNull) + { + throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); + } + + /// + /// + /// + /// + /// + /// + /// + public static BsonBinaryData ToBinData(int field, BsonBinarySubType subtype, ConvertBinDataFormat format) + { + throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); + } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public static BsonBinaryData ToBinData(int field, BsonBinarySubType subtype, ConvertBinDataFormat format, + int onError, int onNull) + { + throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); + } + + /// + /// + /// + /// + /// + /// + /// + public static BsonBinaryData ToBinData(long field, BsonBinarySubType subtype, ConvertBinDataFormat format) + { + throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); + } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public static BsonBinaryData ToBinData(long field, BsonBinarySubType subtype, ConvertBinDataFormat format, + long onError, long onNull) + { + throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); + } + + /// + /// + /// + /// + /// + /// + /// + public static BsonBinaryData ToBinData(double field, BsonBinarySubType subtype, ConvertBinDataFormat format) + { + throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); + } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public static BsonBinaryData ToBinData(double field, BsonBinarySubType subtype, ConvertBinDataFormat format, + double onError, double onNull) + { + throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); + } + + /// + /// + /// public enum ConvertBinDataFormat { + /// + /// + /// base64, + /// + /// + /// base64url, + /// + /// + /// utf8, + /// + /// + /// hex, + /// + /// + /// uuid, } } diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs new file mode 100644 index 00000000000..6398f3f38c1 --- /dev/null +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs @@ -0,0 +1,65 @@ +/* Copyright 2010-present MongoDB Inc. + * + * 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.Collections.Generic; +using System.Linq; +using FluentAssertions; +using MongoDB.Bson; +using MongoDB.Driver.Linq; +using MongoDB.Driver.TestHelpers; +using Xunit; + +namespace MongoDB.Driver.Tests.Linq.Linq3Implementation.Translators.ExpressionToAggregationExpressionTranslators.MethodTranslators +{ + + public class ConvertMethodToAggregationExpressionTranslatorTests : + LinqIntegrationTest + { + public ConvertMethodToAggregationExpressionTranslatorTests(ClassFixture fixture) + : base(fixture) + { + } + + [Fact] + public void Test1() + { + var collection = Fixture.Collection; + + var queryable = collection.AsQueryable() + .Select(x => + Mql.ToBinData(x.StringProperty, BsonBinarySubType.Binary, Mql.ConvertBinDataFormat.base64)); + + var expectedStages = + new[] + { + "{ $project : { _v : { $dateFromString : { dateString : '$S' } }, _id : 0 } }" + }; + + var stages = Translate(collection, queryable); + AssertStages(stages, expectedStages); + } + + + public sealed class ClassFixture : MongoCollectionFixture + { + protected override IEnumerable InitialData { get; } + } + + public class TestClass + { + public string StringProperty { get; set; } + } + } +} \ No newline at end of file diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/DateFromStringMethodToAggregationExpressionTranslatorTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/DateFromStringMethodToAggregationExpressionTranslatorTests.cs index 69dae706333..e774a7b0b80 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/DateFromStringMethodToAggregationExpressionTranslatorTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/DateFromStringMethodToAggregationExpressionTranslatorTests.cs @@ -21,7 +21,6 @@ using MongoDB.Bson; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.TestHelpers.XunitExtensions; -using MongoDB.Driver.Linq; using Xunit; namespace MongoDB.Driver.Tests.Linq.Linq3Implementation.Translators.ExpressionToAggregationExpressionTranslators.MethodTranslators From 748496739a825333c81e6be31989d271d79d6863 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Thu, 13 Mar 2025 16:40:46 +0100 Subject: [PATCH 03/82] Fixed errors --- ...onvertMethodToAggregationExpressionTranslator.cs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs index ac12715d7fa..ef682f75070 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs @@ -64,16 +64,25 @@ public static TranslatedExpression Translate(TranslationContext context, MethodC var formatTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, formatExpression); var formatAst = formatTranslation.Ast; + AstExpression ast; if (method.IsOneOf(__withOnErrorAndOnNullMethods)) { var onErrorExpression = arguments[3]; var onErrorTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, onErrorExpression); var onErrorAst = onErrorTranslation.Ast; - //TODO Continue + var onNullExpression = arguments[4]; + var onNullTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, onNullExpression); + var onNullAst = onNullTranslation.Ast; + + ast = AstExpression.Convert(fieldAst, AstExpression.Constant(BsonType.Binary), onErrorAst, onNullAst, + subType: subTypeAst, format: formatAst); + } + else + { + ast = AstExpression.Convert(fieldAst, AstExpression.Constant(BsonType.Binary), subType: subTypeAst, format: formatAst); } - var ast = AstExpression.Convert(fieldAst, AstExpression.Constant(BsonType.Binary), subType: subTypeAst, format: formatAst); return new TranslatedExpression(expression, ast, resultSerializer); } } From d604e2a7641d2efce9781df68646e91190f6f060 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Fri, 14 Mar 2025 10:25:24 +0100 Subject: [PATCH 04/82] Small rename --- .../Reflection/MqlMethod.cs | 78 ++++++++--- ...MethodToAggregationExpressionTranslator.cs | 3 +- src/MongoDB.Driver/Mql.cs | 130 ++++++++++++++++-- ...dToAggregationExpressionTranslatorTests.cs | 2 +- 4 files changed, 175 insertions(+), 38 deletions(-) diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/MqlMethod.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/MqlMethod.cs index d13d22fe31c..0cc875bc7f9 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/MqlMethod.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/MqlMethod.cs @@ -33,14 +33,23 @@ internal static class MqlMethod private static readonly MethodInfo __field; private static readonly MethodInfo __isMissing; private static readonly MethodInfo __isNullOrMissing; - private static readonly MethodInfo __toBinDataFromString; - private static readonly MethodInfo __toBinDataFromInt; - private static readonly MethodInfo __toBinDataFromLong; private static readonly MethodInfo __toBinDataFromDouble; - private static readonly MethodInfo __toBinDataFromStringWithOnErrorAndOnNull; + + private static readonly MethodInfo __toBinDataFromDoubleWithOnErrorAndOnNull; + private static readonly MethodInfo __toBinDataFromInt; private static readonly MethodInfo __toBinDataFromIntWithOnErrorAndOnNull; + private static readonly MethodInfo __toBinDataFromLong; private static readonly MethodInfo __toBinDataFromLongWithOnErrorAndOnNull; - private static readonly MethodInfo __toBinDataFromDoubleWithOnErrorAndOnNull; + private static readonly MethodInfo __toBinDataFromString; + private static readonly MethodInfo __toBinDataFromStringWithOnErrorAndOnNull; + private static readonly MethodInfo __toDoubleFromBinData; + private static readonly MethodInfo __toDoubleFromBinDataWithOnErrorAndOnNull; + private static readonly MethodInfo __toIntFromBinData; + private static readonly MethodInfo __toIntFromBinDataWithOnErrorAndOnNull; + private static readonly MethodInfo __toLongFromBinData; + private static readonly MethodInfo __toLongFromBinDataWithOnErrorAndOnNull; + private static readonly MethodInfo __toStringFromBinData; + private static readonly MethodInfo __toStringFromBinDataWithOnErrorAndOnNull; // static constructor static MqlMethod() @@ -55,18 +64,32 @@ static MqlMethod() __field = ReflectionInfo.Method((object container, string fieldName, IBsonSerializer serializer) => Mql.Field(container, fieldName, serializer)); __isMissing = ReflectionInfo.Method((object field) => Mql.IsMissing(field)); __isNullOrMissing = ReflectionInfo.Method((object field) => Mql.IsNullOrMissing(field)); - __toBinDataFromString = ReflectionInfo.Method((string field, BsonBinarySubType subType, Mql.ConvertBinDataFormat format) => Mql.ToBinData(field, subType, format)); - __toBinDataFromInt = ReflectionInfo.Method((int field, BsonBinarySubType subType, Mql.ConvertBinDataFormat format) => Mql.ToBinData(field, subType, format)); - __toBinDataFromLong = ReflectionInfo.Method((long field, BsonBinarySubType subType, Mql.ConvertBinDataFormat format) => Mql.ToBinData(field, subType, format)); - __toBinDataFromDouble = ReflectionInfo.Method((double field, BsonBinarySubType subType, Mql.ConvertBinDataFormat format) => Mql.ToBinData(field, subType, format)); - __toBinDataFromStringWithOnErrorAndOnNull = ReflectionInfo.Method((string field, BsonBinarySubType subType, Mql.ConvertBinDataFormat format, string onError, string onNull) - => Mql.ToBinData(field, subType, format, onError, onNull)); - __toBinDataFromIntWithOnErrorAndOnNull = ReflectionInfo.Method((int field, BsonBinarySubType subType, Mql.ConvertBinDataFormat format, int onError, int onNull) - => Mql.ToBinData(field, subType, format, onError, onNull)); - __toBinDataFromLongWithOnErrorAndOnNull = ReflectionInfo.Method((long field, BsonBinarySubType subType, Mql.ConvertBinDataFormat format, long onError, long onNull) - => Mql.ToBinData(field, subType, format, onError, onNull)); - __toBinDataFromDoubleWithOnErrorAndOnNull = ReflectionInfo.Method((double field, BsonBinarySubType subType, Mql.ConvertBinDataFormat format, double onError, double onNull) - => Mql.ToBinData(field, subType, format, onError, onNull)); + + // Convert methods + __toBinDataFromDouble = ReflectionInfo.Method((double field, BsonBinarySubType subType, Mql.ConvertBinDataFormat format) => Mql.ConvertToBinData(field, subType, format)); + __toBinDataFromDoubleWithOnErrorAndOnNull = ReflectionInfo.Method((double field, BsonBinarySubType subType, Mql.ConvertBinDataFormat format, BsonBinaryData onError, BsonBinaryData onNull) + => Mql.ConvertToBinData(field, subType, format, onError, onNull)); + __toBinDataFromInt = ReflectionInfo.Method((int field, BsonBinarySubType subType, Mql.ConvertBinDataFormat format) => Mql.ConvertToBinData(field, subType, format)); + __toBinDataFromIntWithOnErrorAndOnNull = ReflectionInfo.Method((int field, BsonBinarySubType subType, Mql.ConvertBinDataFormat format, BsonBinaryData onError, BsonBinaryData onNull) + => Mql.ConvertToBinData(field, subType, format, onError, onNull)); + __toBinDataFromLong = ReflectionInfo.Method((long field, BsonBinarySubType subType, Mql.ConvertBinDataFormat format) => Mql.ConvertToBinData(field, subType, format)); + __toBinDataFromLongWithOnErrorAndOnNull = ReflectionInfo.Method((long field, BsonBinarySubType subType, Mql.ConvertBinDataFormat format, BsonBinaryData onError, BsonBinaryData onNull) + => Mql.ConvertToBinData(field, subType, format, onError, onNull)); + __toBinDataFromString = ReflectionInfo.Method((string field, BsonBinarySubType subType, Mql.ConvertBinDataFormat format) => Mql.ConvertToBinData(field, subType, format)); + __toBinDataFromStringWithOnErrorAndOnNull = ReflectionInfo.Method((string field, BsonBinarySubType subType, Mql.ConvertBinDataFormat format, BsonBinaryData onError, BsonBinaryData onNull) + => Mql.ConvertToBinData(field, subType, format, onError, onNull)); + + __toDoubleFromBinData = ReflectionInfo.Method((BsonBinaryData field, Mql.ConvertBinDataFormat format) => Mql.ToDouble(field, format)); + __toDoubleFromBinDataWithOnErrorAndOnNull = ReflectionInfo.Method((BsonBinaryData field, Mql.ConvertBinDataFormat format, double? onError, double? onNull) => Mql.ToDouble(field, format, onError, onNull)); + + __toIntFromBinData = ReflectionInfo.Method((BsonBinaryData field, Mql.ConvertBinDataFormat format) => Mql.ConvertToInt(field, format)); + __toIntFromBinDataWithOnErrorAndOnNull = ReflectionInfo.Method((BsonBinaryData field, Mql.ConvertBinDataFormat format, int? onError, int? onNull) => Mql.ToInt(field, format, onError, onNull)); + + __toLongFromBinData = ReflectionInfo.Method((BsonBinaryData field, Mql.ConvertBinDataFormat format) => Mql.ConvertToLong(field, format)); + __toLongFromBinDataWithOnErrorAndOnNull = ReflectionInfo.Method((BsonBinaryData field, Mql.ConvertBinDataFormat format, long? onError, long? onNull) => Mql.ConvertToLong(field, format, onError, onNull)); + + __toStringFromBinData = ReflectionInfo.Method((BsonBinaryData field, Mql.ConvertBinDataFormat format) => Mql.ConvertToString(field, format)); + __toStringFromBinDataWithOnErrorAndOnNull = ReflectionInfo.Method((BsonBinaryData field, Mql.ConvertBinDataFormat format, string onError, string onNull) => Mql.ConvertToString(field, format, onError, onNull)); } // public properties @@ -80,13 +103,24 @@ static MqlMethod() public static MethodInfo Field => __field; public static MethodInfo IsMissing => __isMissing; public static MethodInfo IsNullOrMissing => __isNullOrMissing; - public static MethodInfo ToBinDataFromString => __toBinDataFromString; - public static MethodInfo ToBinDataFromInt => __toBinDataFromInt; - public static MethodInfo ToBinDataFromLong => __toBinDataFromLong; public static MethodInfo ToBinDataFromDouble => __toBinDataFromDouble; - public static MethodInfo ToBinDataFromStringWithOnErrorAndOnNull => __toBinDataFromStringWithOnErrorAndOnNull; + public static MethodInfo ToBinDataFromDoubleWithOnErrorAndOnNull => __toBinDataFromDoubleWithOnErrorAndOnNull; + + public static MethodInfo ToBinDataFromInt => __toBinDataFromInt; public static MethodInfo ToBinDataFromIntWithOnErrorAndOnNull => __toBinDataFromIntWithOnErrorAndOnNull; + + public static MethodInfo ToBinDataFromLong => __toBinDataFromLong; public static MethodInfo ToBinDataFromLongWithOnErrorAndOnNull => __toBinDataFromLongWithOnErrorAndOnNull; - public static MethodInfo ToBinDataFromDoubleWithOnErrorAndOnNull => __toBinDataFromDoubleWithOnErrorAndOnNull; + + public static MethodInfo ToBinDataFromString => __toBinDataFromString; + public static MethodInfo ToBinDataFromStringWithOnErrorAndOnNull => __toBinDataFromStringWithOnErrorAndOnNull; + public static MethodInfo ToDoubleFromBinData => __toDoubleFromBinData; + public static MethodInfo ToDoubleFromBinDataWithOnErrorAndOnNull => __toDoubleFromBinDataWithOnErrorAndOnNull; + public static MethodInfo ToIntFromBinData => __toIntFromBinData; + public static MethodInfo ToIntFromBinDataWithOnErrorAndOnNull => __toIntFromBinDataWithOnErrorAndOnNull; + public static MethodInfo ToLongFromBinData => __toLongFromBinData; + public static MethodInfo ToLongFromBinDataWithOnErrorAndOnNull => __toLongFromBinDataWithOnErrorAndOnNull; + public static MethodInfo ToStringFromBinData => __toStringFromBinData; + public static MethodInfo ToStringFromBinDataWithOnErrorAndOnNull => __toStringFromBinDataWithOnErrorAndOnNull; } } diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs index ef682f75070..eb398a1b9c5 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs @@ -54,7 +54,6 @@ public static TranslatedExpression Translate(TranslationContext context, MethodC var fieldExpression = arguments[0]; var fieldTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, fieldExpression); var fieldAst = fieldTranslation.Ast; - var resultSerializer = BsonBinaryDataSerializer.Instance; var subTypeExpression = arguments[1]; var subTypeTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, subTypeExpression); @@ -83,7 +82,7 @@ public static TranslatedExpression Translate(TranslationContext context, MethodC ast = AstExpression.Convert(fieldAst, AstExpression.Constant(BsonType.Binary), subType: subTypeAst, format: formatAst); } - return new TranslatedExpression(expression, ast, resultSerializer); + return new TranslatedExpression(expression, ast, BsonBinaryDataSerializer.Instance); } } } \ No newline at end of file diff --git a/src/MongoDB.Driver/Mql.cs b/src/MongoDB.Driver/Mql.cs index 3f9cad6e945..2532adbddd6 100644 --- a/src/MongoDB.Driver/Mql.cs +++ b/src/MongoDB.Driver/Mql.cs @@ -160,7 +160,7 @@ public static bool IsNullOrMissing(TField field) /// /// /// - public static BsonBinaryData ToBinData(string field, BsonBinarySubType subtype, ConvertBinDataFormat format) + public static BsonBinaryData ConvertToBinData(string field, BsonBinarySubType subtype, ConvertBinDataFormat format) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } @@ -175,8 +175,8 @@ public static BsonBinaryData ToBinData(string field, BsonBinarySubType subtype, /// /// /// - public static BsonBinaryData ToBinData(string field, BsonBinarySubType subtype, ConvertBinDataFormat format, - string onError, string onNull) + public static BsonBinaryData ConvertToBinData(string field, BsonBinarySubType subtype, ConvertBinDataFormat format, + BsonBinaryData onError, BsonBinaryData onNull) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } @@ -188,7 +188,7 @@ public static BsonBinaryData ToBinData(string field, BsonBinarySubType subtype, /// /// /// - public static BsonBinaryData ToBinData(int field, BsonBinarySubType subtype, ConvertBinDataFormat format) + public static BsonBinaryData ConvertToBinData(int field, BsonBinarySubType subtype, ConvertBinDataFormat format) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } @@ -203,8 +203,8 @@ public static BsonBinaryData ToBinData(int field, BsonBinarySubType subtype, Con /// /// /// - public static BsonBinaryData ToBinData(int field, BsonBinarySubType subtype, ConvertBinDataFormat format, - int onError, int onNull) + public static BsonBinaryData ConvertToBinData(int field, BsonBinarySubType subtype, ConvertBinDataFormat format, + BsonBinaryData onError, BsonBinaryData onNull) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } @@ -216,7 +216,7 @@ public static BsonBinaryData ToBinData(int field, BsonBinarySubType subtype, Con /// /// /// - public static BsonBinaryData ToBinData(long field, BsonBinarySubType subtype, ConvertBinDataFormat format) + public static BsonBinaryData ConvertToBinData(long field, BsonBinarySubType subtype, ConvertBinDataFormat format) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } @@ -231,8 +231,8 @@ public static BsonBinaryData ToBinData(long field, BsonBinarySubType subtype, Co /// /// /// - public static BsonBinaryData ToBinData(long field, BsonBinarySubType subtype, ConvertBinDataFormat format, - long onError, long onNull) + public static BsonBinaryData ConvertToBinData(long field, BsonBinarySubType subtype, ConvertBinDataFormat format, + BsonBinaryData onError, BsonBinaryData onNull) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } @@ -244,7 +244,7 @@ public static BsonBinaryData ToBinData(long field, BsonBinarySubType subtype, Co /// /// /// - public static BsonBinaryData ToBinData(double field, BsonBinarySubType subtype, ConvertBinDataFormat format) + public static BsonBinaryData ConvertToBinData(double field, BsonBinarySubType subtype, ConvertBinDataFormat format) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } @@ -259,8 +259,8 @@ public static BsonBinaryData ToBinData(double field, BsonBinarySubType subtype, /// /// /// - public static BsonBinaryData ToBinData(double field, BsonBinarySubType subtype, ConvertBinDataFormat format, - double onError, double onNull) + public static BsonBinaryData ConvertToBinData(double field, BsonBinarySubType subtype, ConvertBinDataFormat format, + BsonBinaryData onError, BsonBinaryData onNull) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } @@ -268,7 +268,111 @@ public static BsonBinaryData ToBinData(double field, BsonBinarySubType subtype, /// /// /// - public enum ConvertBinDataFormat + /// + /// + /// + /// + public static string ConvertToString(BsonBinaryData field, ConvertBinDataFormat format) + { + throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); + } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + public static string ConvertToString(BsonBinaryData field, ConvertBinDataFormat format, string onError, string onNull) + { + throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); + } + + /// + /// + /// + /// + /// + /// + /// + public static int? ConvertToInt(BsonBinaryData field, ConvertBinDataFormat format) + { + throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); + } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + public static int? ToInt(BsonBinaryData field, ConvertBinDataFormat format, int? onError, int? onNull) + { + throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); + } + + /// + /// + /// + /// + /// + /// + /// + public static long? ConvertToLong(BsonBinaryData field, ConvertBinDataFormat format) + { + throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); + } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + public static long? ConvertToLong(BsonBinaryData field, ConvertBinDataFormat format, long? onError, long? onNull) + { + throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); + } + + /// + /// + /// + /// + /// + /// + /// + public static double? ToDouble(BsonBinaryData field, ConvertBinDataFormat format) + { + throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); + } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + public static double? ToDouble(BsonBinaryData field, ConvertBinDataFormat format, double? onError, double? onNull) + { + throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); + } + + /// + /// + /// + public enum ConvertBinDataFormat //TODO Fix naming { /// /// diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs index 6398f3f38c1..1a0a74b5976 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs @@ -39,7 +39,7 @@ public void Test1() var queryable = collection.AsQueryable() .Select(x => - Mql.ToBinData(x.StringProperty, BsonBinarySubType.Binary, Mql.ConvertBinDataFormat.base64)); + Mql.ConvertToBinData(x.StringProperty, BsonBinarySubType.Binary, Mql.ConvertBinDataFormat.base64)); var expectedStages = new[] From e46ebc6b97f6800e79fcf5b78d99251d4bfe1699 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Fri, 14 Mar 2025 12:29:05 +0100 Subject: [PATCH 05/82] Various corrections --- .../Reflection/MqlMethod.cs | 99 ++++++------- ...essionToAggregationExpressionTranslator.cs | 10 +- ...MethodToAggregationExpressionTranslator.cs | 139 +++++++++++++----- 3 files changed, 159 insertions(+), 89 deletions(-) diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/MqlMethod.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/MqlMethod.cs index 0cc875bc7f9..14429e04e28 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/MqlMethod.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/MqlMethod.cs @@ -33,23 +33,23 @@ internal static class MqlMethod private static readonly MethodInfo __field; private static readonly MethodInfo __isMissing; private static readonly MethodInfo __isNullOrMissing; - private static readonly MethodInfo __toBinDataFromDouble; - private static readonly MethodInfo __toBinDataFromDoubleWithOnErrorAndOnNull; - private static readonly MethodInfo __toBinDataFromInt; - private static readonly MethodInfo __toBinDataFromIntWithOnErrorAndOnNull; - private static readonly MethodInfo __toBinDataFromLong; - private static readonly MethodInfo __toBinDataFromLongWithOnErrorAndOnNull; - private static readonly MethodInfo __toBinDataFromString; - private static readonly MethodInfo __toBinDataFromStringWithOnErrorAndOnNull; - private static readonly MethodInfo __toDoubleFromBinData; - private static readonly MethodInfo __toDoubleFromBinDataWithOnErrorAndOnNull; - private static readonly MethodInfo __toIntFromBinData; - private static readonly MethodInfo __toIntFromBinDataWithOnErrorAndOnNull; - private static readonly MethodInfo __toLongFromBinData; - private static readonly MethodInfo __toLongFromBinDataWithOnErrorAndOnNull; - private static readonly MethodInfo __toStringFromBinData; - private static readonly MethodInfo __toStringFromBinDataWithOnErrorAndOnNull; + private static readonly MethodInfo __convertToBinDataFromDouble; + private static readonly MethodInfo __convertToBinDataFromDoubleWithOnErrorAndOnNull; + private static readonly MethodInfo __convertToBinDataFromInt; + private static readonly MethodInfo __convertToBinDataFromIntWithOnErrorAndOnNull; + private static readonly MethodInfo __convertToBinDataFromLong; + private static readonly MethodInfo __convertToBinDataFromLongWithOnErrorAndOnNull; + private static readonly MethodInfo __convertToBinDataFromString; + private static readonly MethodInfo __convertToBinDataFromStringWithOnErrorAndOnNull; + private static readonly MethodInfo __convertToDoubleFromBinData; + private static readonly MethodInfo __convertToDoubleFromBinDataWithOnErrorAndOnNull; + private static readonly MethodInfo __convertToIntFromBinData; + private static readonly MethodInfo __convertToIntFromBinDataWithOnErrorAndOnNull; + private static readonly MethodInfo __convertToLongFromBinData; + private static readonly MethodInfo __convertToLongFromBinDataWithOnErrorAndOnNull; + private static readonly MethodInfo __convertToStringFromBinData; + private static readonly MethodInfo __convertToStringFromBinDataWithOnErrorAndOnNull; // static constructor static MqlMethod() @@ -66,30 +66,30 @@ static MqlMethod() __isNullOrMissing = ReflectionInfo.Method((object field) => Mql.IsNullOrMissing(field)); // Convert methods - __toBinDataFromDouble = ReflectionInfo.Method((double field, BsonBinarySubType subType, Mql.ConvertBinDataFormat format) => Mql.ConvertToBinData(field, subType, format)); - __toBinDataFromDoubleWithOnErrorAndOnNull = ReflectionInfo.Method((double field, BsonBinarySubType subType, Mql.ConvertBinDataFormat format, BsonBinaryData onError, BsonBinaryData onNull) + __convertToBinDataFromDouble = ReflectionInfo.Method((double field, BsonBinarySubType subType, Mql.ConvertBinDataFormat format) => Mql.ConvertToBinData(field, subType, format)); + __convertToBinDataFromDoubleWithOnErrorAndOnNull = ReflectionInfo.Method((double field, BsonBinarySubType subType, Mql.ConvertBinDataFormat format, BsonBinaryData onError, BsonBinaryData onNull) => Mql.ConvertToBinData(field, subType, format, onError, onNull)); - __toBinDataFromInt = ReflectionInfo.Method((int field, BsonBinarySubType subType, Mql.ConvertBinDataFormat format) => Mql.ConvertToBinData(field, subType, format)); - __toBinDataFromIntWithOnErrorAndOnNull = ReflectionInfo.Method((int field, BsonBinarySubType subType, Mql.ConvertBinDataFormat format, BsonBinaryData onError, BsonBinaryData onNull) + __convertToBinDataFromInt = ReflectionInfo.Method((int field, BsonBinarySubType subType, Mql.ConvertBinDataFormat format) => Mql.ConvertToBinData(field, subType, format)); + __convertToBinDataFromIntWithOnErrorAndOnNull = ReflectionInfo.Method((int field, BsonBinarySubType subType, Mql.ConvertBinDataFormat format, BsonBinaryData onError, BsonBinaryData onNull) => Mql.ConvertToBinData(field, subType, format, onError, onNull)); - __toBinDataFromLong = ReflectionInfo.Method((long field, BsonBinarySubType subType, Mql.ConvertBinDataFormat format) => Mql.ConvertToBinData(field, subType, format)); - __toBinDataFromLongWithOnErrorAndOnNull = ReflectionInfo.Method((long field, BsonBinarySubType subType, Mql.ConvertBinDataFormat format, BsonBinaryData onError, BsonBinaryData onNull) + __convertToBinDataFromLong = ReflectionInfo.Method((long field, BsonBinarySubType subType, Mql.ConvertBinDataFormat format) => Mql.ConvertToBinData(field, subType, format)); + __convertToBinDataFromLongWithOnErrorAndOnNull = ReflectionInfo.Method((long field, BsonBinarySubType subType, Mql.ConvertBinDataFormat format, BsonBinaryData onError, BsonBinaryData onNull) => Mql.ConvertToBinData(field, subType, format, onError, onNull)); - __toBinDataFromString = ReflectionInfo.Method((string field, BsonBinarySubType subType, Mql.ConvertBinDataFormat format) => Mql.ConvertToBinData(field, subType, format)); - __toBinDataFromStringWithOnErrorAndOnNull = ReflectionInfo.Method((string field, BsonBinarySubType subType, Mql.ConvertBinDataFormat format, BsonBinaryData onError, BsonBinaryData onNull) + __convertToBinDataFromString = ReflectionInfo.Method((string field, BsonBinarySubType subType, Mql.ConvertBinDataFormat format) => Mql.ConvertToBinData(field, subType, format)); + __convertToBinDataFromStringWithOnErrorAndOnNull = ReflectionInfo.Method((string field, BsonBinarySubType subType, Mql.ConvertBinDataFormat format, BsonBinaryData onError, BsonBinaryData onNull) => Mql.ConvertToBinData(field, subType, format, onError, onNull)); - __toDoubleFromBinData = ReflectionInfo.Method((BsonBinaryData field, Mql.ConvertBinDataFormat format) => Mql.ToDouble(field, format)); - __toDoubleFromBinDataWithOnErrorAndOnNull = ReflectionInfo.Method((BsonBinaryData field, Mql.ConvertBinDataFormat format, double? onError, double? onNull) => Mql.ToDouble(field, format, onError, onNull)); + __convertToDoubleFromBinData = ReflectionInfo.Method((BsonBinaryData field, Mql.ConvertBinDataFormat format) => Mql.ToDouble(field, format)); + __convertToDoubleFromBinDataWithOnErrorAndOnNull = ReflectionInfo.Method((BsonBinaryData field, Mql.ConvertBinDataFormat format, double? onError, double? onNull) => Mql.ToDouble(field, format, onError, onNull)); - __toIntFromBinData = ReflectionInfo.Method((BsonBinaryData field, Mql.ConvertBinDataFormat format) => Mql.ConvertToInt(field, format)); - __toIntFromBinDataWithOnErrorAndOnNull = ReflectionInfo.Method((BsonBinaryData field, Mql.ConvertBinDataFormat format, int? onError, int? onNull) => Mql.ToInt(field, format, onError, onNull)); + __convertToIntFromBinData = ReflectionInfo.Method((BsonBinaryData field, Mql.ConvertBinDataFormat format) => Mql.ConvertToInt(field, format)); + __convertToIntFromBinDataWithOnErrorAndOnNull = ReflectionInfo.Method((BsonBinaryData field, Mql.ConvertBinDataFormat format, int? onError, int? onNull) => Mql.ToInt(field, format, onError, onNull)); - __toLongFromBinData = ReflectionInfo.Method((BsonBinaryData field, Mql.ConvertBinDataFormat format) => Mql.ConvertToLong(field, format)); - __toLongFromBinDataWithOnErrorAndOnNull = ReflectionInfo.Method((BsonBinaryData field, Mql.ConvertBinDataFormat format, long? onError, long? onNull) => Mql.ConvertToLong(field, format, onError, onNull)); + __convertToLongFromBinData = ReflectionInfo.Method((BsonBinaryData field, Mql.ConvertBinDataFormat format) => Mql.ConvertToLong(field, format)); + __convertToLongFromBinDataWithOnErrorAndOnNull = ReflectionInfo.Method((BsonBinaryData field, Mql.ConvertBinDataFormat format, long? onError, long? onNull) => Mql.ConvertToLong(field, format, onError, onNull)); - __toStringFromBinData = ReflectionInfo.Method((BsonBinaryData field, Mql.ConvertBinDataFormat format) => Mql.ConvertToString(field, format)); - __toStringFromBinDataWithOnErrorAndOnNull = ReflectionInfo.Method((BsonBinaryData field, Mql.ConvertBinDataFormat format, string onError, string onNull) => Mql.ConvertToString(field, format, onError, onNull)); + __convertToStringFromBinData = ReflectionInfo.Method((BsonBinaryData field, Mql.ConvertBinDataFormat format) => Mql.ConvertToString(field, format)); + __convertToStringFromBinDataWithOnErrorAndOnNull = ReflectionInfo.Method((BsonBinaryData field, Mql.ConvertBinDataFormat format, string onError, string onNull) => Mql.ConvertToString(field, format, onError, onNull)); } // public properties @@ -103,24 +103,21 @@ static MqlMethod() public static MethodInfo Field => __field; public static MethodInfo IsMissing => __isMissing; public static MethodInfo IsNullOrMissing => __isNullOrMissing; - public static MethodInfo ToBinDataFromDouble => __toBinDataFromDouble; - public static MethodInfo ToBinDataFromDoubleWithOnErrorAndOnNull => __toBinDataFromDoubleWithOnErrorAndOnNull; - - public static MethodInfo ToBinDataFromInt => __toBinDataFromInt; - public static MethodInfo ToBinDataFromIntWithOnErrorAndOnNull => __toBinDataFromIntWithOnErrorAndOnNull; - - public static MethodInfo ToBinDataFromLong => __toBinDataFromLong; - public static MethodInfo ToBinDataFromLongWithOnErrorAndOnNull => __toBinDataFromLongWithOnErrorAndOnNull; - - public static MethodInfo ToBinDataFromString => __toBinDataFromString; - public static MethodInfo ToBinDataFromStringWithOnErrorAndOnNull => __toBinDataFromStringWithOnErrorAndOnNull; - public static MethodInfo ToDoubleFromBinData => __toDoubleFromBinData; - public static MethodInfo ToDoubleFromBinDataWithOnErrorAndOnNull => __toDoubleFromBinDataWithOnErrorAndOnNull; - public static MethodInfo ToIntFromBinData => __toIntFromBinData; - public static MethodInfo ToIntFromBinDataWithOnErrorAndOnNull => __toIntFromBinDataWithOnErrorAndOnNull; - public static MethodInfo ToLongFromBinData => __toLongFromBinData; - public static MethodInfo ToLongFromBinDataWithOnErrorAndOnNull => __toLongFromBinDataWithOnErrorAndOnNull; - public static MethodInfo ToStringFromBinData => __toStringFromBinData; - public static MethodInfo ToStringFromBinDataWithOnErrorAndOnNull => __toStringFromBinDataWithOnErrorAndOnNull; + public static MethodInfo ConvertToBinDataFromDouble => __convertToBinDataFromDouble; + public static MethodInfo ConvertToBinDataFromDoubleWithOnErrorAndOnNull => __convertToBinDataFromDoubleWithOnErrorAndOnNull; + public static MethodInfo ConvertToBinDataFromInt => __convertToBinDataFromInt; + public static MethodInfo ConvertToBinDataFromIntWithOnErrorAndOnNull => __convertToBinDataFromIntWithOnErrorAndOnNull; + public static MethodInfo ConvertToBinDataFromLong => __convertToBinDataFromLong; + public static MethodInfo ConvertToBinDataFromLongWithOnErrorAndOnNull => __convertToBinDataFromLongWithOnErrorAndOnNull; + public static MethodInfo ConvertToBinDataFromString => __convertToBinDataFromString; + public static MethodInfo ConvertToBinDataFromStringWithOnErrorAndOnNull => __convertToBinDataFromStringWithOnErrorAndOnNull; + public static MethodInfo ConvertToDoubleFromBinData => __convertToDoubleFromBinData; + public static MethodInfo ConvertToDoubleFromBinDataWithOnErrorAndOnNull => __convertToDoubleFromBinDataWithOnErrorAndOnNull; + public static MethodInfo ConvertToIntFromBinData => __convertToIntFromBinData; + public static MethodInfo ConvertToIntFromBinDataWithOnErrorAndOnNull => __convertToIntFromBinDataWithOnErrorAndOnNull; + public static MethodInfo ConvertToLongFromBinData => __convertToLongFromBinData; + public static MethodInfo ConvertToLongFromBinDataWithOnErrorAndOnNull => __convertToLongFromBinDataWithOnErrorAndOnNull; + public static MethodInfo ConvertToStringFromBinData => __convertToStringFromBinData; + public static MethodInfo ConvertToStringFromBinDataWithOnErrorAndOnNull => __convertToStringFromBinDataWithOnErrorAndOnNull; } } diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodCallExpressionToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodCallExpressionToAggregationExpressionTranslator.cs index b44804acd0c..33c12aeeb86 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodCallExpressionToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodCallExpressionToAggregationExpressionTranslator.cs @@ -138,6 +138,13 @@ public static TranslatedExpression Translate(TranslationContext context, MethodC case "LongCount": return CountMethodToAggregationExpressionTranslator.Translate(context, expression); + case "ConvertToBinData": + case "ConvertToDouble": + case "ConvertToInt": + case "ConvertToLong": + case "ConvertToString": + return ConvertMethodToAggregationExpressionTranslator.Translate(context, expression); + case "ElementAt": case "ElementAtOrDefault": return ElementAtMethodToAggregationExpressionTranslator.Translate(context, expression); @@ -203,9 +210,6 @@ public static TranslatedExpression Translate(TranslationContext context, MethodC case "TrimEnd": case "TrimStart": return TrimMethodToAggregationExpressionTranslator.Translate(context, expression); - - case "ToBinData": - return ConvertMethodToAggregationExpressionTranslator.Translate(context, expression); } throw new ExpressionNotSupportedException(expression); diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs index eb398a1b9c5..a65cab68c6e 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs @@ -25,64 +25,133 @@ namespace MongoDB.Driver.Linq.Linq3Implementation.Translators.ExpressionToAggreg { internal class ConvertMethodToAggregationExpressionTranslator { - private static readonly MethodInfo[] __toBinDataMethods = - { - MqlMethod.ToBinDataFromString, - MqlMethod.ToBinDataFromInt, - MqlMethod.ToBinDataFromLong, - MqlMethod.ToBinDataFromDouble - }; + private static readonly MethodInfo[] __convertToBinDataMethods = + [ + MqlMethod.ConvertToBinDataFromString, + MqlMethod.ConvertToBinDataFromInt, + MqlMethod.ConvertToBinDataFromLong, + MqlMethod.ConvertToBinDataFromDouble + ]; - private static readonly MethodInfo[] __withOnErrorAndOnNullMethods = - { - MqlMethod.ToBinDataFromStringWithOnErrorAndOnNull, - MqlMethod.ToBinDataFromIntWithOnErrorAndOnNull, - MqlMethod.ToBinDataFromLongWithOnErrorAndOnNull, - MqlMethod.ToBinDataFromDoubleWithOnErrorAndOnNull - }; + private static readonly MethodInfo[] __convertToBinDataWithOnErrorAndOnNullMethods = + [ + MqlMethod.ConvertToBinDataFromStringWithOnErrorAndOnNull, + MqlMethod.ConvertToBinDataFromIntWithOnErrorAndOnNull, + MqlMethod.ConvertToBinDataFromLongWithOnErrorAndOnNull, + MqlMethod.ConvertToBinDataFromDoubleWithOnErrorAndOnNull + ]; + + private static readonly MethodInfo[] __convertToStringMethods = + [ + MqlMethod.ConvertToStringFromBinData + ]; + + private static readonly MethodInfo[] __convertToStringWithOnErrorAndOnNullMethods = + [ + MqlMethod.ConvertToStringFromBinDataWithOnErrorAndOnNull + ]; + + private static readonly MethodInfo[] __convertToIntMethods = + [ + MqlMethod.ConvertToIntFromBinData + ]; + + private static readonly MethodInfo[] __convertToIntWithOnErrorAndOnNullMethods = + [ + MqlMethod.ConvertToIntFromBinDataWithOnErrorAndOnNull + ]; + + private static readonly MethodInfo[] __convertToLongMethods = + [ + MqlMethod.ConvertToLongFromBinData + ]; + + private static readonly MethodInfo[] __convertToLongWithOnErrorAndOnNullMethods = + [ + MqlMethod.ConvertToLongFromBinDataWithOnErrorAndOnNull + ]; + + private static readonly MethodInfo[] __convertToDoubleMethods = + [ + MqlMethod.ConvertToDoubleFromBinData + ]; + + private static readonly MethodInfo[] __convertToDoubleWithOnErrorAndOnNullMethods = + [ + MqlMethod.ConvertToDoubleFromBinDataWithOnErrorAndOnNull + ]; public static TranslatedExpression Translate(TranslationContext context, MethodCallExpression expression) { var method = expression.Method; var arguments = expression.Arguments; - if (!method.IsOneOf(__toBinDataMethods, __withOnErrorAndOnNullMethods)) + if (!method.IsOneOf(__convertToBinDataMethods, __convertToBinDataWithOnErrorAndOnNullMethods, + __convertToStringMethods, __convertToStringWithOnErrorAndOnNullMethods, + __convertToIntMethods, __convertToIntWithOnErrorAndOnNullMethods, + __convertToLongMethods, __convertToLongWithOnErrorAndOnNullMethods, + __convertToDoubleMethods, __convertToDoubleWithOnErrorAndOnNullMethods)) { throw new ExpressionNotSupportedException(expression); } + AstExpression fieldAst = null; + AstExpression subTypeAst = null; + AstExpression formatAst = null; + AstExpression onErrorAst = null; + AstExpression onNullAst = null; + + var subTypeIndex = -1; + var formatIndex = -1; + var onErrorIndex = -1; + var onNullIndex = -1; + var fieldExpression = arguments[0]; var fieldTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, fieldExpression); - var fieldAst = fieldTranslation.Ast; + fieldAst = fieldTranslation.Ast; - var subTypeExpression = arguments[1]; - var subTypeTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, subTypeExpression); - var subTypeAst = subTypeTranslation.Ast; + if (method.IsOneOf(__convertToBinDataMethods, __convertToBinDataWithOnErrorAndOnNullMethods)) + { + subTypeIndex = 1; + formatIndex = 2; - var formatExpression = arguments[2]; - var formatTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, formatExpression); - var formatAst = formatTranslation.Ast; + if (method.IsOneOf(__convertToBinDataWithOnErrorAndOnNullMethods)) + { + onErrorIndex = 3; + onNullIndex = 4; + } + } - AstExpression ast; - if (method.IsOneOf(__withOnErrorAndOnNullMethods)) + if (subTypeIndex > 0) + { + var subTypeExpression = arguments[subTypeIndex]; + var subTypeTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, subTypeExpression); + subTypeAst = subTypeTranslation.Ast; + } + if (formatIndex > 0) { - var onErrorExpression = arguments[3]; + var formatExpression = arguments[formatIndex]; + var formatTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, formatExpression); + formatAst = formatTranslation.Ast; + } + if (onErrorIndex > 0) + { + var onErrorExpression = arguments[onErrorIndex]; var onErrorTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, onErrorExpression); - var onErrorAst = onErrorTranslation.Ast; - - var onNullExpression = arguments[4]; - var onNullTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, onNullExpression); - var onNullAst = onNullTranslation.Ast; - - ast = AstExpression.Convert(fieldAst, AstExpression.Constant(BsonType.Binary), onErrorAst, onNullAst, - subType: subTypeAst, format: formatAst); + onErrorAst = onErrorTranslation.Ast; } - else + if (onNullIndex > 0) { - ast = AstExpression.Convert(fieldAst, AstExpression.Constant(BsonType.Binary), subType: subTypeAst, format: formatAst); + var onNullExpression = arguments[onNullIndex]; + var onNullTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, onNullExpression); + onNullAst = onNullTranslation.Ast; } + + var ast = AstExpression.Convert(fieldAst, AstExpression.Constant(BsonType.Binary), subType: subTypeAst, format: formatAst, onError: onErrorAst, onNull: onNullAst); return new TranslatedExpression(expression, ast, BsonBinaryDataSerializer.Instance); + + } } } \ No newline at end of file From 9ff23479dbed7bfe562398ca672c37074aacf52e Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Fri, 14 Mar 2025 13:29:41 +0100 Subject: [PATCH 06/82] Behaviour correction --- .../Ast/Expressions/AstExpression.cs | 10 +-- ...MethodToAggregationExpressionTranslator.cs | 69 +++++++++++++++++-- 2 files changed, 70 insertions(+), 9 deletions(-) diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstExpression.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstExpression.cs index b49561bb90d..dbef53c9799 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstExpression.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstExpression.cs @@ -261,10 +261,9 @@ public static AstExpression Convert(AstExpression input, AstExpression to, AstEx Ensure.IsNotNull(input, nameof(input)); Ensure.IsNotNull(to, nameof(to)); - if (to is AstConstantExpression toConstantExpression && - (toConstantExpression.Value as BsonString)?.Value is { } toValue && - onError == null && - onNull == null) + if (onError == null && onNull == null && subType == null && format == null && + to is AstConstantExpression toConstantExpression && + (toConstantExpression.Value as BsonString)?.Value is { } toValue) { var unaryOperator = toValue switch { @@ -278,13 +277,14 @@ public static AstExpression Convert(AstExpression input, AstExpression to, AstEx "string" => AstUnaryOperator.ToString, _ => (AstUnaryOperator?)null }; + if (unaryOperator.HasValue) { return AstExpression.Unary(unaryOperator.Value, input); } } - return new AstConvertExpression(input, to, onError, onNull); + return new AstConvertExpression(input, to, onError, onNull, subType, format); } public static AstExpression DateAdd( diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs index a65cab68c6e..4df628b31bc 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs @@ -16,6 +16,7 @@ using System.Linq.Expressions; using System.Reflection; using MongoDB.Bson; +using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Serializers; using MongoDB.Driver.Linq.Linq3Implementation.Ast.Expressions; using MongoDB.Driver.Linq.Linq3Implementation.Misc; @@ -95,11 +96,13 @@ public static TranslatedExpression Translate(TranslationContext context, MethodC throw new ExpressionNotSupportedException(expression); } + BsonType toType = BsonType.Null; AstExpression fieldAst = null; AstExpression subTypeAst = null; AstExpression formatAst = null; AstExpression onErrorAst = null; AstExpression onNullAst = null; + IBsonSerializer serializer = null; var subTypeIndex = -1; var formatIndex = -1; @@ -112,6 +115,9 @@ public static TranslatedExpression Translate(TranslationContext context, MethodC if (method.IsOneOf(__convertToBinDataMethods, __convertToBinDataWithOnErrorAndOnNullMethods)) { + serializer = BsonBinaryDataSerializer.Instance; + toType = BsonType.Binary; + subTypeIndex = 1; formatIndex = 2; @@ -122,6 +128,62 @@ public static TranslatedExpression Translate(TranslationContext context, MethodC } } + if (method.IsOneOf(__convertToStringMethods, __convertToStringWithOnErrorAndOnNullMethods)) + { + serializer = StringSerializer.Instance; + toType = BsonType.String; + + formatIndex = 1; + + if (method.IsOneOf(__convertToStringWithOnErrorAndOnNullMethods)) + { + onErrorIndex = 2; + onNullIndex = 3; + } + } + + if (method.IsOneOf(__convertToIntMethods, __convertToIntWithOnErrorAndOnNullMethods)) + { + serializer = new NullableSerializer(Int32Serializer.Instance); + toType = BsonType.Int32; + + formatIndex = 1; + + if (method.IsOneOf(__convertToIntWithOnErrorAndOnNullMethods)) + { + onErrorIndex = 2; + onNullIndex = 3; + } + } + + if (method.IsOneOf(__convertToLongMethods, __convertToLongWithOnErrorAndOnNullMethods)) + { + serializer = new NullableSerializer(Int64Serializer.Instance); + toType = BsonType.Int64; + + formatIndex = 1; + + if (method.IsOneOf(__convertToLongWithOnErrorAndOnNullMethods)) + { + onErrorIndex = 2; + onNullIndex = 3; + } + } + + if (method.IsOneOf(__convertToDoubleMethods, __convertToDoubleWithOnErrorAndOnNullMethods)) + { + serializer = StringSerializer.Instance; + toType = BsonType.Double; + + formatIndex = 1; + + if (method.IsOneOf(__convertToDoubleWithOnErrorAndOnNullMethods)) + { + onErrorIndex = 2; + onNullIndex = 3; + } + } + if (subTypeIndex > 0) { var subTypeExpression = arguments[subTypeIndex]; @@ -147,11 +209,10 @@ public static TranslatedExpression Translate(TranslationContext context, MethodC onNullAst = onNullTranslation.Ast; } + var toAst = AstExpression.Constant(toType); - var ast = AstExpression.Convert(fieldAst, AstExpression.Constant(BsonType.Binary), subType: subTypeAst, format: formatAst, onError: onErrorAst, onNull: onNullAst); - return new TranslatedExpression(expression, ast, BsonBinaryDataSerializer.Instance); - - + var ast = AstExpression.Convert(fieldAst, toAst, subType: subTypeAst, format: formatAst, onError: onErrorAst, onNull: onNullAst); + return new TranslatedExpression(expression, ast, serializer); } } } \ No newline at end of file From 3d555964056849d6457500fe3fa6274a6e20fbc9 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Fri, 14 Mar 2025 13:41:36 +0100 Subject: [PATCH 07/82] Simplified code --- ...MethodToAggregationExpressionTranslator.cs | 221 +++--------------- 1 file changed, 26 insertions(+), 195 deletions(-) diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs index 4df628b31bc..8bbd8ff940d 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs @@ -1,18 +1,5 @@ -/* Copyright 2010-present MongoDB Inc. - * - * 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.Collections.Generic; +using System.Linq; using System.Linq.Expressions; using System.Reflection; using MongoDB.Bson; @@ -26,193 +13,37 @@ namespace MongoDB.Driver.Linq.Linq3Implementation.Translators.ExpressionToAggreg { internal class ConvertMethodToAggregationExpressionTranslator { - private static readonly MethodInfo[] __convertToBinDataMethods = - [ - MqlMethod.ConvertToBinDataFromString, - MqlMethod.ConvertToBinDataFromInt, - MqlMethod.ConvertToBinDataFromLong, - MqlMethod.ConvertToBinDataFromDouble - ]; - - private static readonly MethodInfo[] __convertToBinDataWithOnErrorAndOnNullMethods = - [ - MqlMethod.ConvertToBinDataFromStringWithOnErrorAndOnNull, - MqlMethod.ConvertToBinDataFromIntWithOnErrorAndOnNull, - MqlMethod.ConvertToBinDataFromLongWithOnErrorAndOnNull, - MqlMethod.ConvertToBinDataFromDoubleWithOnErrorAndOnNull - ]; - - private static readonly MethodInfo[] __convertToStringMethods = - [ - MqlMethod.ConvertToStringFromBinData - ]; - - private static readonly MethodInfo[] __convertToStringWithOnErrorAndOnNullMethods = - [ - MqlMethod.ConvertToStringFromBinDataWithOnErrorAndOnNull - ]; - - private static readonly MethodInfo[] __convertToIntMethods = - [ - MqlMethod.ConvertToIntFromBinData - ]; - - private static readonly MethodInfo[] __convertToIntWithOnErrorAndOnNullMethods = - [ - MqlMethod.ConvertToIntFromBinDataWithOnErrorAndOnNull - ]; - - private static readonly MethodInfo[] __convertToLongMethods = - [ - MqlMethod.ConvertToLongFromBinData - ]; - - private static readonly MethodInfo[] __convertToLongWithOnErrorAndOnNullMethods = - [ - MqlMethod.ConvertToLongFromBinDataWithOnErrorAndOnNull - ]; - - private static readonly MethodInfo[] __convertToDoubleMethods = - [ - MqlMethod.ConvertToDoubleFromBinData - ]; - - private static readonly MethodInfo[] __convertToDoubleWithOnErrorAndOnNullMethods = - [ - MqlMethod.ConvertToDoubleFromBinDataWithOnErrorAndOnNull - ]; + private static readonly Dictionary _methodMappings = new() + { + { [MqlMethod.ConvertToBinDataFromString, MqlMethod.ConvertToBinDataFromInt, MqlMethod.ConvertToBinDataFromLong, MqlMethod.ConvertToBinDataFromDouble], (BsonBinaryDataSerializer.Instance, BsonType.Binary, 2, 1, null, null) }, + { [MqlMethod.ConvertToBinDataFromStringWithOnErrorAndOnNull, MqlMethod.ConvertToBinDataFromIntWithOnErrorAndOnNull, MqlMethod.ConvertToBinDataFromLongWithOnErrorAndOnNull, MqlMethod.ConvertToBinDataFromDoubleWithOnErrorAndOnNull], (BsonBinaryDataSerializer.Instance, BsonType.Binary, 2, 1, 3, 4) }, + { [MqlMethod.ConvertToStringFromBinData], (StringSerializer.Instance, BsonType.String, 1, null, null, null) }, + { [MqlMethod.ConvertToStringFromBinDataWithOnErrorAndOnNull], (StringSerializer.Instance, BsonType.String, 1, null, 2, 3) }, + { [MqlMethod.ConvertToIntFromBinData], (new NullableSerializer(Int32Serializer.Instance), BsonType.Int32, 1, null, null, null) }, + { [MqlMethod.ConvertToIntFromBinDataWithOnErrorAndOnNull], (new NullableSerializer(Int32Serializer.Instance), BsonType.Int32, 1, null, 2, 3) }, + { [MqlMethod.ConvertToLongFromBinData], (new NullableSerializer(Int64Serializer.Instance), BsonType.Int64, 1, null, null, null) }, + { [MqlMethod.ConvertToLongFromBinDataWithOnErrorAndOnNull], (new NullableSerializer(Int64Serializer.Instance), BsonType.Int64, 1, null, 2, 3) }, + { [MqlMethod.ConvertToDoubleFromBinData], (StringSerializer.Instance, BsonType.String, 1, null, null, null) }, + { [MqlMethod.ConvertToDoubleFromBinDataWithOnErrorAndOnNull], (StringSerializer.Instance, BsonType.String, 1, null, 2, 3) } + }; public static TranslatedExpression Translate(TranslationContext context, MethodCallExpression expression) { var method = expression.Method; var arguments = expression.Arguments; - - if (!method.IsOneOf(__convertToBinDataMethods, __convertToBinDataWithOnErrorAndOnNullMethods, - __convertToStringMethods, __convertToStringWithOnErrorAndOnNullMethods, - __convertToIntMethods, __convertToIntWithOnErrorAndOnNullMethods, - __convertToLongMethods, __convertToLongWithOnErrorAndOnNullMethods, - __convertToDoubleMethods, __convertToDoubleWithOnErrorAndOnNullMethods)) - { + + var mapping = _methodMappings.FirstOrDefault(m => m.Key.Contains(method)).Value; + if (mapping.Serializer == null) throw new ExpressionNotSupportedException(expression); - } - - BsonType toType = BsonType.Null; - AstExpression fieldAst = null; - AstExpression subTypeAst = null; - AstExpression formatAst = null; - AstExpression onErrorAst = null; - AstExpression onNullAst = null; - IBsonSerializer serializer = null; - - var subTypeIndex = -1; - var formatIndex = -1; - var onErrorIndex = -1; - var onNullIndex = -1; - - var fieldExpression = arguments[0]; - var fieldTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, fieldExpression); - fieldAst = fieldTranslation.Ast; - - if (method.IsOneOf(__convertToBinDataMethods, __convertToBinDataWithOnErrorAndOnNullMethods)) - { - serializer = BsonBinaryDataSerializer.Instance; - toType = BsonType.Binary; - - subTypeIndex = 1; - formatIndex = 2; - - if (method.IsOneOf(__convertToBinDataWithOnErrorAndOnNullMethods)) - { - onErrorIndex = 3; - onNullIndex = 4; - } - } - - if (method.IsOneOf(__convertToStringMethods, __convertToStringWithOnErrorAndOnNullMethods)) - { - serializer = StringSerializer.Instance; - toType = BsonType.String; - - formatIndex = 1; - - if (method.IsOneOf(__convertToStringWithOnErrorAndOnNullMethods)) - { - onErrorIndex = 2; - onNullIndex = 3; - } - } - - if (method.IsOneOf(__convertToIntMethods, __convertToIntWithOnErrorAndOnNullMethods)) - { - serializer = new NullableSerializer(Int32Serializer.Instance); - toType = BsonType.Int32; - - formatIndex = 1; - - if (method.IsOneOf(__convertToIntWithOnErrorAndOnNullMethods)) - { - onErrorIndex = 2; - onNullIndex = 3; - } - } - - if (method.IsOneOf(__convertToLongMethods, __convertToLongWithOnErrorAndOnNullMethods)) - { - serializer = new NullableSerializer(Int64Serializer.Instance); - toType = BsonType.Int64; - - formatIndex = 1; - - if (method.IsOneOf(__convertToLongWithOnErrorAndOnNullMethods)) - { - onErrorIndex = 2; - onNullIndex = 3; - } - } - - if (method.IsOneOf(__convertToDoubleMethods, __convertToDoubleWithOnErrorAndOnNullMethods)) - { - serializer = StringSerializer.Instance; - toType = BsonType.Double; - - formatIndex = 1; - - if (method.IsOneOf(__convertToDoubleWithOnErrorAndOnNullMethods)) - { - onErrorIndex = 2; - onNullIndex = 3; - } - } - - if (subTypeIndex > 0) - { - var subTypeExpression = arguments[subTypeIndex]; - var subTypeTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, subTypeExpression); - subTypeAst = subTypeTranslation.Ast; - } - if (formatIndex > 0) - { - var formatExpression = arguments[formatIndex]; - var formatTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, formatExpression); - formatAst = formatTranslation.Ast; - } - if (onErrorIndex > 0) - { - var onErrorExpression = arguments[onErrorIndex]; - var onErrorTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, onErrorExpression); - onErrorAst = onErrorTranslation.Ast; - } - if (onNullIndex > 0) - { - var onNullExpression = arguments[onNullIndex]; - var onNullTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, onNullExpression); - onNullAst = onNullTranslation.Ast; - } - var toAst = AstExpression.Constant(toType); + var fieldAst = ExpressionToAggregationExpressionTranslator.Translate(context, arguments[0]).Ast; + var subTypeAst = mapping.SubTypeIndex.HasValue ? ExpressionToAggregationExpressionTranslator.Translate(context, arguments[mapping.SubTypeIndex.Value]).Ast : null; + var formatAst = ExpressionToAggregationExpressionTranslator.Translate(context, arguments[mapping.FormatIndex]).Ast; + var onErrorAst = mapping.OnErrorIndex.HasValue ? ExpressionToAggregationExpressionTranslator.Translate(context, arguments[mapping.OnErrorIndex.Value]).Ast : null; + var onNullAst = mapping.OnNullIndex.HasValue ? ExpressionToAggregationExpressionTranslator.Translate(context, arguments[mapping.OnNullIndex.Value]).Ast : null; - var ast = AstExpression.Convert(fieldAst, toAst, subType: subTypeAst, format: formatAst, onError: onErrorAst, onNull: onNullAst); - return new TranslatedExpression(expression, ast, serializer); + var ast = AstExpression.Convert(fieldAst, AstExpression.Constant(mapping.Type), onError: onErrorAst, onNull: onNullAst, subType: subTypeAst, format: formatAst); + return new TranslatedExpression(expression, ast, mapping.Serializer); } } -} \ No newline at end of file +} From ebe1bf5fb5a211268af99d6074e9815dcd916db1 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Fri, 14 Mar 2025 14:01:41 +0100 Subject: [PATCH 08/82] Small improvements --- ...MethodToAggregationExpressionTranslator.cs | 42 +++++++++++-------- src/MongoDB.Driver/Mql.cs | 3 +- 2 files changed, 26 insertions(+), 19 deletions(-) diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs index 8bbd8ff940d..590081baf0f 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs @@ -6,42 +6,48 @@ using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Serializers; using MongoDB.Driver.Linq.Linq3Implementation.Ast.Expressions; -using MongoDB.Driver.Linq.Linq3Implementation.Misc; +using MongoDB.Driver.Linq.Linq3Implementation.ExtensionMethods; using MongoDB.Driver.Linq.Linq3Implementation.Reflection; namespace MongoDB.Driver.Linq.Linq3Implementation.Translators.ExpressionToAggregationExpressionTranslators.MethodTranslators { internal class ConvertMethodToAggregationExpressionTranslator { - private static readonly Dictionary _methodMappings = new() - { - { [MqlMethod.ConvertToBinDataFromString, MqlMethod.ConvertToBinDataFromInt, MqlMethod.ConvertToBinDataFromLong, MqlMethod.ConvertToBinDataFromDouble], (BsonBinaryDataSerializer.Instance, BsonType.Binary, 2, 1, null, null) }, - { [MqlMethod.ConvertToBinDataFromStringWithOnErrorAndOnNull, MqlMethod.ConvertToBinDataFromIntWithOnErrorAndOnNull, MqlMethod.ConvertToBinDataFromLongWithOnErrorAndOnNull, MqlMethod.ConvertToBinDataFromDoubleWithOnErrorAndOnNull], (BsonBinaryDataSerializer.Instance, BsonType.Binary, 2, 1, 3, 4) }, - { [MqlMethod.ConvertToStringFromBinData], (StringSerializer.Instance, BsonType.String, 1, null, null, null) }, - { [MqlMethod.ConvertToStringFromBinDataWithOnErrorAndOnNull], (StringSerializer.Instance, BsonType.String, 1, null, 2, 3) }, - { [MqlMethod.ConvertToIntFromBinData], (new NullableSerializer(Int32Serializer.Instance), BsonType.Int32, 1, null, null, null) }, - { [MqlMethod.ConvertToIntFromBinDataWithOnErrorAndOnNull], (new NullableSerializer(Int32Serializer.Instance), BsonType.Int32, 1, null, 2, 3) }, - { [MqlMethod.ConvertToLongFromBinData], (new NullableSerializer(Int64Serializer.Instance), BsonType.Int64, 1, null, null, null) }, - { [MqlMethod.ConvertToLongFromBinDataWithOnErrorAndOnNull], (new NullableSerializer(Int64Serializer.Instance), BsonType.Int64, 1, null, 2, 3) }, - { [MqlMethod.ConvertToDoubleFromBinData], (StringSerializer.Instance, BsonType.String, 1, null, null, null) }, - { [MqlMethod.ConvertToDoubleFromBinDataWithOnErrorAndOnNull], (StringSerializer.Instance, BsonType.String, 1, null, 2, 3) } - }; - + private static readonly List<(MethodInfo[] Methods, IBsonSerializer Serializer, BsonType Type, int? FormatIndex, int? SubTypeIndex, int? OnErrorIndex, int? OnNullIndex)> _methodMappings = + [ + ([MqlMethod.ConvertToBinDataFromString, MqlMethod.ConvertToBinDataFromInt, MqlMethod.ConvertToBinDataFromLong, MqlMethod.ConvertToBinDataFromDouble], BsonBinaryDataSerializer.Instance, BsonType.Binary, 2, 1, null, null), + ([MqlMethod.ConvertToBinDataFromStringWithOnErrorAndOnNull, MqlMethod.ConvertToBinDataFromIntWithOnErrorAndOnNull, MqlMethod.ConvertToBinDataFromLongWithOnErrorAndOnNull, MqlMethod.ConvertToBinDataFromDoubleWithOnErrorAndOnNull], BsonBinaryDataSerializer.Instance, BsonType.Binary, 2, 1, 3, 4), + ([MqlMethod.ConvertToStringFromBinData], StringSerializer.Instance, BsonType.String, 1, null, null, null), + ([MqlMethod.ConvertToStringFromBinDataWithOnErrorAndOnNull], StringSerializer.Instance, BsonType.String, 1, null, 2, 3), + ([MqlMethod.ConvertToIntFromBinData], new NullableSerializer(Int32Serializer.Instance), BsonType.Int32, 1, null, null, null), + ([MqlMethod.ConvertToIntFromBinDataWithOnErrorAndOnNull], new NullableSerializer(Int32Serializer.Instance), BsonType.Int32, 1, null, 2, 3), + ([MqlMethod.ConvertToLongFromBinData], new NullableSerializer(Int64Serializer.Instance), BsonType.Int64, 1, null, null, null), + ([MqlMethod.ConvertToLongFromBinDataWithOnErrorAndOnNull], new NullableSerializer(Int64Serializer.Instance), BsonType.Int64, 1, null, 2, 3), + ([MqlMethod.ConvertToDoubleFromBinData], StringSerializer.Instance, BsonType.Double, 1, null, null, null), + ([MqlMethod.ConvertToDoubleFromBinDataWithOnErrorAndOnNull], StringSerializer.Instance, BsonType.Double, 1, null, 2, 3) + ]; public static TranslatedExpression Translate(TranslationContext context, MethodCallExpression expression) { var method = expression.Method; var arguments = expression.Arguments; - var mapping = _methodMappings.FirstOrDefault(m => m.Key.Contains(method)).Value; - if (mapping.Serializer == null) + var mapping = _methodMappings.FirstOrDefault(m => m.Methods.Contains(method)); + if (mapping == default) throw new ExpressionNotSupportedException(expression); var fieldAst = ExpressionToAggregationExpressionTranslator.Translate(context, arguments[0]).Ast; var subTypeAst = mapping.SubTypeIndex.HasValue ? ExpressionToAggregationExpressionTranslator.Translate(context, arguments[mapping.SubTypeIndex.Value]).Ast : null; - var formatAst = ExpressionToAggregationExpressionTranslator.Translate(context, arguments[mapping.FormatIndex]).Ast; var onErrorAst = mapping.OnErrorIndex.HasValue ? ExpressionToAggregationExpressionTranslator.Translate(context, arguments[mapping.OnErrorIndex.Value]).Ast : null; var onNullAst = mapping.OnNullIndex.HasValue ? ExpressionToAggregationExpressionTranslator.Translate(context, arguments[mapping.OnNullIndex.Value]).Ast : null; + AstExpression formatAst = null; + if (mapping.FormatIndex.HasValue) + { + var formatExpression = arguments[mapping.FormatIndex.Value]; + var formatEnum = formatExpression.GetConstantValue(expression); + formatAst = AstExpression.Constant(formatEnum.ToString()); + } + var ast = AstExpression.Convert(fieldAst, AstExpression.Constant(mapping.Type), onError: onErrorAst, onNull: onNullAst, subType: subTypeAst, format: formatAst); return new TranslatedExpression(expression, ast, mapping.Serializer); } diff --git a/src/MongoDB.Driver/Mql.cs b/src/MongoDB.Driver/Mql.cs index 2532adbddd6..94217968f3d 100644 --- a/src/MongoDB.Driver/Mql.cs +++ b/src/MongoDB.Driver/Mql.cs @@ -372,7 +372,8 @@ public static string ConvertToString(BsonBinaryData field, ConvertBinDataFormat /// /// /// - public enum ConvertBinDataFormat //TODO Fix naming + public enum ConvertBinDataFormat + //TODO Decide: if to use, location, naming { /// /// From 20763ce7ec36cbe300a3da44973ac36f4016ada0 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Mon, 17 Mar 2025 16:41:37 +0100 Subject: [PATCH 09/82] Added test --- ...dToAggregationExpressionTranslatorTests.cs | 77 ++++++++++++++++++- 1 file changed, 76 insertions(+), 1 deletion(-) diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs index 1a0a74b5976..66ed4e0630c 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs @@ -13,10 +13,13 @@ * limitations under the License. */ +using System; using System.Collections.Generic; using System.Linq; using FluentAssertions; using MongoDB.Bson; +using MongoDB.Driver.Core.Misc; +using MongoDB.Driver.Core.TestHelpers.XunitExtensions; using MongoDB.Driver.Linq; using MongoDB.Driver.TestHelpers; using Xunit; @@ -51,15 +54,87 @@ public void Test1() AssertStages(stages, expectedStages); } + [Fact] + public void MongoDBFunctions_ConvertToStringFromBson_should_work() + { + RequireServer.Check().Supports(Feature.ConvertBinDataToFromNumeric); + + var id = 1; + + var collection = Fixture.Collection; + var queryable = collection.AsQueryable() + .Where(x => x.Id == id) + .Select(x => Mql.ConvertToString(x.BinaryProperty, Mql.ConvertBinDataFormat.uuid)); + + var expectedStages = + new[] + { + $"{{ $match : {{ _id : {id} }} }}", + """{"$project": { "_v" : { "$convert" : { "input" : "$BinaryProperty", "to" : 2, "format" : "uuid" } }, "_id" : 0 }}""", + }; + + var stages = Translate(collection, queryable); + AssertStages(stages, expectedStages); + + var expectedResult = "867dee52-c331-484e-92d1-c56479b8e67e"; + + var result = queryable.Single(); + Assert.Equal(expectedResult, result); + } + + [Fact] + public void MongoDBFunctions_ConvertToStringFromBsonWithOnErrorAndOnNull_should_work() + { + RequireServer.Check().Supports(Feature.ConvertBinDataToFromNumeric); + + var id = 0; + + var collection = Fixture.Collection; + var queryable = collection.AsQueryable() + .Where(x => x.Id == id); + //.Select(x => Mql.ConvertToString(x.BinaryProperty, Mql.ConvertBinDataFormat.hex, "onError", "onNull")); + + var expectedStages = + new[] + { + $"{{ $match : {{ _id : {id} }} }}", + //"""{"$project": { "_v" : { "$convert" : { "input" : "$BinaryProperty", "to" : 2, "onError": "onError", "onNull": "onNull", "format" : "hex" } }, "_id" : 0 }}""", + }; + + var stages = Translate(collection, queryable); + AssertStages(stages, expectedStages); + + var expectedResult = "867dee52-c331-484e-92d1-c56479b8e67e"; + + var result = queryable.Single(); + Assert.Equal(expectedResult, result.StringProperty); + } + + /** + * + * What to test + * + */ + public sealed class ClassFixture : MongoCollectionFixture { - protected override IEnumerable InitialData { get; } + protected override IEnumerable InitialData { get; } = + [ + new TestClass {Id = 0 }, + new TestClass {Id = 1, BinaryProperty = new BsonBinaryData(Guid.Parse("867dee52-c331-484e-92d1-c56479b8e67e"), GuidRepresentation.Standard)}, + ]; } public class TestClass { + public int Id { get; set; } + public BsonBinaryData BinaryProperty { get; set; } + public double DoubleProperty { get; set; } + public int IntProperty { get; set; } + public long LongProperty { get; set; } public string StringProperty { get; set; } + } } } \ No newline at end of file From 64b0082997b97b151b8e3e768adf13796a788be7 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Mon, 17 Mar 2025 19:42:32 +0100 Subject: [PATCH 10/82] Several improvements --- src/MongoDB.Driver/Core/Misc/Feature.cs | 6 + .../Reflection/MqlMethod.cs | 32 ++-- ...MethodToAggregationExpressionTranslator.cs | 14 +- src/MongoDB.Driver/Mql.cs | 60 ++------ ...dToAggregationExpressionTranslatorTests.cs | 141 ++++++++++++------ 5 files changed, 141 insertions(+), 112 deletions(-) diff --git a/src/MongoDB.Driver/Core/Misc/Feature.cs b/src/MongoDB.Driver/Core/Misc/Feature.cs index 720cf3c19db..a1a14aa90cf 100644 --- a/src/MongoDB.Driver/Core/Misc/Feature.cs +++ b/src/MongoDB.Driver/Core/Misc/Feature.cs @@ -44,6 +44,7 @@ public class Feature private static readonly Feature __clientBulkWrite = new Feature("ClientBulkWrite", WireVersion.Server80); private static readonly Feature __clientSideEncryption = new Feature("ClientSideEncryption", WireVersion.Server42); private static readonly Feature __clusteredIndexes = new Feature("ClusteredIndexes", WireVersion.Server53); + private static readonly Feature __convertBinDataToFromNumeric = new Feature("ConvertBinDataToFromNumeric", WireVersion.Server81); private static readonly Feature __createIndexCommitQuorum = new Feature("CreateIndexCommitQuorum", WireVersion.Server44); private static readonly Feature __createIndexesUsingInsertOperations = new Feature("CreateIndexesUsingInsertOperations", WireVersion.Zero, WireVersion.Server42); private static readonly Feature __csfleRangeAlgorithm = new Feature("CsfleRangeAlgorithm", WireVersion.Server62); @@ -193,6 +194,11 @@ public class Feature /// public static Feature ClusteredIndexes => __clusteredIndexes; + /// + /// Gets the conversion of binary data to/from numeric types feature. + /// + public static Feature ConvertBinDataToFromNumeric => __convertBinDataToFromNumeric; + /// /// Gets the create index commit quorum feature. /// diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/MqlMethod.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/MqlMethod.cs index 14429e04e28..a1eae566efb 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/MqlMethod.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/MqlMethod.cs @@ -66,30 +66,30 @@ static MqlMethod() __isNullOrMissing = ReflectionInfo.Method((object field) => Mql.IsNullOrMissing(field)); // Convert methods - __convertToBinDataFromDouble = ReflectionInfo.Method((double field, BsonBinarySubType subType, Mql.ConvertBinDataFormat format) => Mql.ConvertToBinData(field, subType, format)); - __convertToBinDataFromDoubleWithOnErrorAndOnNull = ReflectionInfo.Method((double field, BsonBinarySubType subType, Mql.ConvertBinDataFormat format, BsonBinaryData onError, BsonBinaryData onNull) + __convertToBinDataFromDouble = ReflectionInfo.Method((double field, BsonBinarySubType subType, string format) => Mql.ConvertToBinData(field, subType, format)); + __convertToBinDataFromDoubleWithOnErrorAndOnNull = ReflectionInfo.Method((double field, BsonBinarySubType subType, string format, BsonBinaryData onError, BsonBinaryData onNull) => Mql.ConvertToBinData(field, subType, format, onError, onNull)); - __convertToBinDataFromInt = ReflectionInfo.Method((int field, BsonBinarySubType subType, Mql.ConvertBinDataFormat format) => Mql.ConvertToBinData(field, subType, format)); - __convertToBinDataFromIntWithOnErrorAndOnNull = ReflectionInfo.Method((int field, BsonBinarySubType subType, Mql.ConvertBinDataFormat format, BsonBinaryData onError, BsonBinaryData onNull) + __convertToBinDataFromInt = ReflectionInfo.Method((int field, BsonBinarySubType subType, string format) => Mql.ConvertToBinData(field, subType, format)); + __convertToBinDataFromIntWithOnErrorAndOnNull = ReflectionInfo.Method((int field, BsonBinarySubType subType, string format, BsonBinaryData onError, BsonBinaryData onNull) => Mql.ConvertToBinData(field, subType, format, onError, onNull)); - __convertToBinDataFromLong = ReflectionInfo.Method((long field, BsonBinarySubType subType, Mql.ConvertBinDataFormat format) => Mql.ConvertToBinData(field, subType, format)); - __convertToBinDataFromLongWithOnErrorAndOnNull = ReflectionInfo.Method((long field, BsonBinarySubType subType, Mql.ConvertBinDataFormat format, BsonBinaryData onError, BsonBinaryData onNull) + __convertToBinDataFromLong = ReflectionInfo.Method((long field, BsonBinarySubType subType, string format) => Mql.ConvertToBinData(field, subType, format)); + __convertToBinDataFromLongWithOnErrorAndOnNull = ReflectionInfo.Method((long field, BsonBinarySubType subType, string format, BsonBinaryData onError, BsonBinaryData onNull) => Mql.ConvertToBinData(field, subType, format, onError, onNull)); - __convertToBinDataFromString = ReflectionInfo.Method((string field, BsonBinarySubType subType, Mql.ConvertBinDataFormat format) => Mql.ConvertToBinData(field, subType, format)); - __convertToBinDataFromStringWithOnErrorAndOnNull = ReflectionInfo.Method((string field, BsonBinarySubType subType, Mql.ConvertBinDataFormat format, BsonBinaryData onError, BsonBinaryData onNull) + __convertToBinDataFromString = ReflectionInfo.Method((string field, BsonBinarySubType subType, string format) => Mql.ConvertToBinData(field, subType, format)); + __convertToBinDataFromStringWithOnErrorAndOnNull = ReflectionInfo.Method((string field, BsonBinarySubType subType, string format, BsonBinaryData onError, BsonBinaryData onNull) => Mql.ConvertToBinData(field, subType, format, onError, onNull)); - __convertToDoubleFromBinData = ReflectionInfo.Method((BsonBinaryData field, Mql.ConvertBinDataFormat format) => Mql.ToDouble(field, format)); - __convertToDoubleFromBinDataWithOnErrorAndOnNull = ReflectionInfo.Method((BsonBinaryData field, Mql.ConvertBinDataFormat format, double? onError, double? onNull) => Mql.ToDouble(field, format, onError, onNull)); + __convertToDoubleFromBinData = ReflectionInfo.Method((BsonBinaryData field, string format) => Mql.ConvertToDouble(field, format)); + __convertToDoubleFromBinDataWithOnErrorAndOnNull = ReflectionInfo.Method((BsonBinaryData field, string format, double? onError, double? onNull) => Mql.ConvertToDouble(field, format, onError, onNull)); - __convertToIntFromBinData = ReflectionInfo.Method((BsonBinaryData field, Mql.ConvertBinDataFormat format) => Mql.ConvertToInt(field, format)); - __convertToIntFromBinDataWithOnErrorAndOnNull = ReflectionInfo.Method((BsonBinaryData field, Mql.ConvertBinDataFormat format, int? onError, int? onNull) => Mql.ToInt(field, format, onError, onNull)); + __convertToIntFromBinData = ReflectionInfo.Method((BsonBinaryData field, string format) => Mql.ConvertToInt(field, format)); + __convertToIntFromBinDataWithOnErrorAndOnNull = ReflectionInfo.Method((BsonBinaryData field, string format, int? onError, int? onNull) => Mql.ToInt(field, format, onError, onNull)); - __convertToLongFromBinData = ReflectionInfo.Method((BsonBinaryData field, Mql.ConvertBinDataFormat format) => Mql.ConvertToLong(field, format)); - __convertToLongFromBinDataWithOnErrorAndOnNull = ReflectionInfo.Method((BsonBinaryData field, Mql.ConvertBinDataFormat format, long? onError, long? onNull) => Mql.ConvertToLong(field, format, onError, onNull)); + __convertToLongFromBinData = ReflectionInfo.Method((BsonBinaryData field, string format) => Mql.ConvertToLong(field, format)); + __convertToLongFromBinDataWithOnErrorAndOnNull = ReflectionInfo.Method((BsonBinaryData field, string format, long? onError, long? onNull) => Mql.ConvertToLong(field, format, onError, onNull)); - __convertToStringFromBinData = ReflectionInfo.Method((BsonBinaryData field, Mql.ConvertBinDataFormat format) => Mql.ConvertToString(field, format)); - __convertToStringFromBinDataWithOnErrorAndOnNull = ReflectionInfo.Method((BsonBinaryData field, Mql.ConvertBinDataFormat format, string onError, string onNull) => Mql.ConvertToString(field, format, onError, onNull)); + __convertToStringFromBinData = ReflectionInfo.Method((BsonBinaryData field, string format) => Mql.ConvertToString(field, format)); + __convertToStringFromBinDataWithOnErrorAndOnNull = ReflectionInfo.Method((BsonBinaryData field, string format, string onError, string onNull) => Mql.ConvertToString(field, format, onError, onNull)); } // public properties diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs index 590081baf0f..a1465c3b0bc 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; @@ -23,8 +24,8 @@ internal class ConvertMethodToAggregationExpressionTranslator ([MqlMethod.ConvertToIntFromBinDataWithOnErrorAndOnNull], new NullableSerializer(Int32Serializer.Instance), BsonType.Int32, 1, null, 2, 3), ([MqlMethod.ConvertToLongFromBinData], new NullableSerializer(Int64Serializer.Instance), BsonType.Int64, 1, null, null, null), ([MqlMethod.ConvertToLongFromBinDataWithOnErrorAndOnNull], new NullableSerializer(Int64Serializer.Instance), BsonType.Int64, 1, null, 2, 3), - ([MqlMethod.ConvertToDoubleFromBinData], StringSerializer.Instance, BsonType.Double, 1, null, null, null), - ([MqlMethod.ConvertToDoubleFromBinDataWithOnErrorAndOnNull], StringSerializer.Instance, BsonType.Double, 1, null, 2, 3) + ([MqlMethod.ConvertToDoubleFromBinData], new NullableSerializer(DoubleSerializer.Instance), BsonType.Double, 1, null, null, null), + ([MqlMethod.ConvertToDoubleFromBinDataWithOnErrorAndOnNull], new NullableSerializer(DoubleSerializer.Instance), BsonType.Double, 1, null, 2, 3) ]; public static TranslatedExpression Translate(TranslationContext context, MethodCallExpression expression) { @@ -36,18 +37,11 @@ public static TranslatedExpression Translate(TranslationContext context, MethodC throw new ExpressionNotSupportedException(expression); var fieldAst = ExpressionToAggregationExpressionTranslator.Translate(context, arguments[0]).Ast; + var formatAst = mapping.FormatIndex.HasValue ? ExpressionToAggregationExpressionTranslator.Translate(context, arguments[mapping.FormatIndex.Value]).Ast : null; var subTypeAst = mapping.SubTypeIndex.HasValue ? ExpressionToAggregationExpressionTranslator.Translate(context, arguments[mapping.SubTypeIndex.Value]).Ast : null; var onErrorAst = mapping.OnErrorIndex.HasValue ? ExpressionToAggregationExpressionTranslator.Translate(context, arguments[mapping.OnErrorIndex.Value]).Ast : null; var onNullAst = mapping.OnNullIndex.HasValue ? ExpressionToAggregationExpressionTranslator.Translate(context, arguments[mapping.OnNullIndex.Value]).Ast : null; - AstExpression formatAst = null; - if (mapping.FormatIndex.HasValue) - { - var formatExpression = arguments[mapping.FormatIndex.Value]; - var formatEnum = formatExpression.GetConstantValue(expression); - formatAst = AstExpression.Constant(formatEnum.ToString()); - } - var ast = AstExpression.Convert(fieldAst, AstExpression.Constant(mapping.Type), onError: onErrorAst, onNull: onNullAst, subType: subTypeAst, format: formatAst); return new TranslatedExpression(expression, ast, mapping.Serializer); } diff --git a/src/MongoDB.Driver/Mql.cs b/src/MongoDB.Driver/Mql.cs index 94217968f3d..c0be30d74ba 100644 --- a/src/MongoDB.Driver/Mql.cs +++ b/src/MongoDB.Driver/Mql.cs @@ -160,7 +160,7 @@ public static bool IsNullOrMissing(TField field) /// /// /// - public static BsonBinaryData ConvertToBinData(string field, BsonBinarySubType subtype, ConvertBinDataFormat format) + public static BsonBinaryData ConvertToBinData(string field, BsonBinarySubType subtype, string format) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } @@ -175,7 +175,7 @@ public static BsonBinaryData ConvertToBinData(string field, BsonBinarySubType su /// /// /// - public static BsonBinaryData ConvertToBinData(string field, BsonBinarySubType subtype, ConvertBinDataFormat format, + public static BsonBinaryData ConvertToBinData(string field, BsonBinarySubType subtype, string format, BsonBinaryData onError, BsonBinaryData onNull) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); @@ -188,7 +188,7 @@ public static BsonBinaryData ConvertToBinData(string field, BsonBinarySubType su /// /// /// - public static BsonBinaryData ConvertToBinData(int field, BsonBinarySubType subtype, ConvertBinDataFormat format) + public static BsonBinaryData ConvertToBinData(int field, BsonBinarySubType subtype, string format) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } @@ -203,7 +203,7 @@ public static BsonBinaryData ConvertToBinData(int field, BsonBinarySubType subty /// /// /// - public static BsonBinaryData ConvertToBinData(int field, BsonBinarySubType subtype, ConvertBinDataFormat format, + public static BsonBinaryData ConvertToBinData(int field, BsonBinarySubType subtype, string format, BsonBinaryData onError, BsonBinaryData onNull) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); @@ -216,7 +216,7 @@ public static BsonBinaryData ConvertToBinData(int field, BsonBinarySubType subty /// /// /// - public static BsonBinaryData ConvertToBinData(long field, BsonBinarySubType subtype, ConvertBinDataFormat format) + public static BsonBinaryData ConvertToBinData(long field, BsonBinarySubType subtype, string format) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } @@ -231,7 +231,7 @@ public static BsonBinaryData ConvertToBinData(long field, BsonBinarySubType subt /// /// /// - public static BsonBinaryData ConvertToBinData(long field, BsonBinarySubType subtype, ConvertBinDataFormat format, + public static BsonBinaryData ConvertToBinData(long field, BsonBinarySubType subtype, string format, BsonBinaryData onError, BsonBinaryData onNull) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); @@ -244,7 +244,7 @@ public static BsonBinaryData ConvertToBinData(long field, BsonBinarySubType subt /// /// /// - public static BsonBinaryData ConvertToBinData(double field, BsonBinarySubType subtype, ConvertBinDataFormat format) + public static BsonBinaryData ConvertToBinData(double field, BsonBinarySubType subtype, string format) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } @@ -259,7 +259,7 @@ public static BsonBinaryData ConvertToBinData(double field, BsonBinarySubType su /// /// /// - public static BsonBinaryData ConvertToBinData(double field, BsonBinarySubType subtype, ConvertBinDataFormat format, + public static BsonBinaryData ConvertToBinData(double field, BsonBinarySubType subtype, string format, BsonBinaryData onError, BsonBinaryData onNull) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); @@ -272,7 +272,7 @@ public static BsonBinaryData ConvertToBinData(double field, BsonBinarySubType su /// /// /// - public static string ConvertToString(BsonBinaryData field, ConvertBinDataFormat format) + public static string ConvertToString(BsonBinaryData field, string format) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } @@ -286,7 +286,7 @@ public static string ConvertToString(BsonBinaryData field, ConvertBinDataFormat /// /// /// - public static string ConvertToString(BsonBinaryData field, ConvertBinDataFormat format, string onError, string onNull) + public static string ConvertToString(BsonBinaryData field, string format, string onError, string onNull) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } @@ -298,7 +298,7 @@ public static string ConvertToString(BsonBinaryData field, ConvertBinDataFormat /// /// /// - public static int? ConvertToInt(BsonBinaryData field, ConvertBinDataFormat format) + public static int? ConvertToInt(BsonBinaryData field, string format) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } @@ -312,7 +312,7 @@ public static string ConvertToString(BsonBinaryData field, ConvertBinDataFormat /// /// /// - public static int? ToInt(BsonBinaryData field, ConvertBinDataFormat format, int? onError, int? onNull) + public static int? ToInt(BsonBinaryData field, string format, int? onError, int? onNull) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } @@ -324,7 +324,7 @@ public static string ConvertToString(BsonBinaryData field, ConvertBinDataFormat /// /// /// - public static long? ConvertToLong(BsonBinaryData field, ConvertBinDataFormat format) + public static long? ConvertToLong(BsonBinaryData field, string format) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } @@ -338,7 +338,7 @@ public static string ConvertToString(BsonBinaryData field, ConvertBinDataFormat /// /// /// - public static long? ConvertToLong(BsonBinaryData field, ConvertBinDataFormat format, long? onError, long? onNull) + public static long? ConvertToLong(BsonBinaryData field, string format, long? onError, long? onNull) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } @@ -350,7 +350,7 @@ public static string ConvertToString(BsonBinaryData field, ConvertBinDataFormat /// /// /// - public static double? ToDouble(BsonBinaryData field, ConvertBinDataFormat format) + public static double? ConvertToDouble(BsonBinaryData field, string format) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } @@ -364,37 +364,9 @@ public static string ConvertToString(BsonBinaryData field, ConvertBinDataFormat /// /// /// - public static double? ToDouble(BsonBinaryData field, ConvertBinDataFormat format, double? onError, double? onNull) + public static double? ConvertToDouble(BsonBinaryData field, string format, double? onError, double? onNull) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } - - /// - /// - /// - public enum ConvertBinDataFormat - //TODO Decide: if to use, location, naming - { - /// - /// - /// - base64, - /// - /// - /// - base64url, - /// - /// - /// - utf8, - /// - /// - /// - hex, - /// - /// - /// - uuid, - } } } diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs index 66ed4e0630c..48c24f60d53 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs @@ -15,9 +15,10 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; -using FluentAssertions; using MongoDB.Bson; +using MongoDB.Bson.Serialization.Attributes; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.TestHelpers.XunitExtensions; using MongoDB.Driver.Linq; @@ -35,104 +36,160 @@ public ConvertMethodToAggregationExpressionTranslatorTests(ClassFixture fixture) { } - [Fact] - public void Test1() + [Theory] + [InlineData(3, -0.5, null)] + [InlineData(2, null, "MongoCommandException")] + public void MongoDBFunctions_ConvertToDoubleFromBson_should_work(int id, double? expectedResult, string expectedException) { - var collection = Fixture.Collection; + RequireServer.Check().Supports(Feature.ConvertBinDataToFromNumeric); + var collection = Fixture.Collection; var queryable = collection.AsQueryable() - .Select(x => - Mql.ConvertToBinData(x.StringProperty, BsonBinarySubType.Binary, Mql.ConvertBinDataFormat.base64)); + .Where(x => x.Id == id) + .Select(x => Mql.ConvertToDouble(x.BinaryProperty, "hex")); var expectedStages = new[] { - "{ $project : { _v : { $dateFromString : { dateString : '$S' } }, _id : 0 } }" + $"{{ $match : {{ _id : {id} }} }}", + $"{{ $project: {{ _v : {{ $convert : {{ input : '$BinaryProperty', to : 1, format : 'hex' }} }}, _id : 0 }} }}", }; - var stages = Translate(collection, queryable); - AssertStages(stages, expectedStages); + AssertOutcome(collection, queryable, expectedStages, expectedResult, expectedException); } - [Fact] - public void MongoDBFunctions_ConvertToStringFromBson_should_work() + [Theory] + [InlineData(2, 15.0, 15.0, null)] + [InlineData(0, 12.0, null, 12.0)] + [InlineData(2, null, null, null)] + [InlineData(0, null, null, null)] + public void MongoDBFunctions_ConvertToDoubleFromBsonWithOnErrorAndOnNull_should_work(int id, double? expectedResult, double? onError, double? onNull) { RequireServer.Check().Supports(Feature.ConvertBinDataToFromNumeric); - var id = 1; - var collection = Fixture.Collection; var queryable = collection.AsQueryable() .Where(x => x.Id == id) - .Select(x => Mql.ConvertToString(x.BinaryProperty, Mql.ConvertBinDataFormat.uuid)); + .Select(x => Mql.ConvertToDouble(x.BinaryProperty, "hex", onError, onNull)); + var onErrorString = onError == null ? "null" : onError.Value.ToString("F1", NumberFormatInfo.InvariantInfo); + var onNullString = onNull == null ? "null" : onNull.Value.ToString("F1", NumberFormatInfo.InvariantInfo); var expectedStages = new[] { $"{{ $match : {{ _id : {id} }} }}", - """{"$project": { "_v" : { "$convert" : { "input" : "$BinaryProperty", "to" : 2, "format" : "uuid" } }, "_id" : 0 }}""", + $"{{ $project: {{ _v : {{ $convert : {{ input : '$BinaryProperty', to : 1, onError: {onErrorString}, onNull: {onNullString}, format : 'hex' }} }}, _id : 0 }} }}", }; - var stages = Translate(collection, queryable); - AssertStages(stages, expectedStages); + AssertOutcome(collection, queryable, expectedStages, expectedResult); + } + + + [Theory] + [InlineData(2, "867dee52-c331-484e-92d1-c56479b8e67e", null)] + [InlineData(1, null, "MongoCommandException")] + public void MongoDBFunctions_ConvertToStringFromBson_should_work(int id, string expectedResult, string expectedException) + { + RequireServer.Check().Supports(Feature.ConvertBinDataToFromNumeric); - var expectedResult = "867dee52-c331-484e-92d1-c56479b8e67e"; + var collection = Fixture.Collection; + var queryable = collection.AsQueryable() + .Where(x => x.Id == id) + .Select(x => Mql.ConvertToString(x.BinaryProperty, "uuid")); + + var expectedStages = + new[] + { + $"{{ $match : {{ _id : {id} }} }}", + """{"$project": { "_v" : { "$convert" : { "input" : "$BinaryProperty", "to" : 2, "format" : "uuid" } }, "_id" : 0 }}""", + }; - var result = queryable.Single(); - Assert.Equal(expectedResult, result); + AssertOutcome(collection, queryable, expectedStages, expectedResult, expectedException); } - [Fact] - public void MongoDBFunctions_ConvertToStringFromBsonWithOnErrorAndOnNull_should_work() + [Theory] + [InlineData(0, "onNull")] + [InlineData(1, "onError")] + public void MongoDBFunctions_ConvertToStringFromBsonWithOnErrorAndOnNull_should_work(int id, string expectedResult) { RequireServer.Check().Supports(Feature.ConvertBinDataToFromNumeric); - var id = 0; - var collection = Fixture.Collection; var queryable = collection.AsQueryable() - .Where(x => x.Id == id); - //.Select(x => Mql.ConvertToString(x.BinaryProperty, Mql.ConvertBinDataFormat.hex, "onError", "onNull")); + .Where(x => x.Id == id) + .Select(x => Mql.ConvertToString(x.BinaryProperty, "uuid", "onError", "onNull")); var expectedStages = new[] { $"{{ $match : {{ _id : {id} }} }}", - //"""{"$project": { "_v" : { "$convert" : { "input" : "$BinaryProperty", "to" : 2, "onError": "onError", "onNull": "onNull", "format" : "hex" } }, "_id" : 0 }}""", + """{"$project": { "_v" : { "$convert" : { "input" : "$BinaryProperty", "to" : 2, "onError": "onError", "onNull": "onNull", "format" : "uuid" } }, "_id" : 0 }}""", }; - var stages = Translate(collection, queryable); - AssertStages(stages, expectedStages); + AssertOutcome(collection, queryable, expectedStages, expectedResult); + } - var expectedResult = "867dee52-c331-484e-92d1-c56479b8e67e"; + private void AssertOutcome(IMongoCollection collection, + IQueryable queryable, + string[] expectedStages, + TResult expectedResult, + string expectedException = null) + { + TResult result = default; - var result = queryable.Single(); - Assert.Equal(expectedResult, result.StringProperty); + var stages = Translate(collection, queryable); + AssertStages(stages, expectedStages); + var exception = Record.Exception(() => result = queryable.Single()); + + if (string.IsNullOrEmpty(expectedException)) + { + Assert.Null(exception); + Assert.Equal(expectedResult, result); + } + else + { + Assert.Equal(expectedException, exception.GetType().Name); + } } - /** - * - * What to test - * - */ - public sealed class ClassFixture : MongoCollectionFixture { protected override IEnumerable InitialData { get; } = [ new TestClass {Id = 0 }, - new TestClass {Id = 1, BinaryProperty = new BsonBinaryData(Guid.Parse("867dee52-c331-484e-92d1-c56479b8e67e"), GuidRepresentation.Standard)}, + new TestClass {Id = 1, BinaryProperty = new BsonBinaryData([0, 1, 2])}, + new TestClass {Id = 2, BinaryProperty = new BsonBinaryData(Guid.Parse("867dee52-c331-484e-92d1-c56479b8e67e"), GuidRepresentation.Standard)}, + new TestClass {Id = 3, BinaryProperty = new BsonBinaryData(Convert.FromBase64String("AAAAAAAA4L8="))} + ]; + + private IEnumerable InitialDataUnTyped { get; } = + [ + BsonDocument.Parse("{ _id : 7 }") ]; + + + //TODO Remove all of this + protected override void InitializeTestCase() + { + base.InitializeTestCase(); + // var collection = Database.GetCollection(Collection.CollectionNamespace.CollectionName); + // collection.InsertMany(InitialDataUnTyped); + } + + public IMongoCollection UnTypedCollection => + Database.GetCollection(Collection.CollectionNamespace.CollectionName); } public class TestClass { public int Id { get; set; } + + [BsonIgnoreIfDefault] public BsonBinaryData BinaryProperty { get; set; } - public double DoubleProperty { get; set; } - public int IntProperty { get; set; } - public long LongProperty { get; set; } + public double? DoubleProperty { get; set; } + public int? IntProperty { get; set; } + public long? LongProperty { get; set; } public string StringProperty { get; set; } } From be829c9ca6d340cfb1b3f6b6995aeddbeb71f934 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Tue, 18 Mar 2025 09:23:49 +0100 Subject: [PATCH 11/82] Added int tests --- .../Reflection/MqlMethod.cs | 2 +- src/MongoDB.Driver/Mql.cs | 2 +- ...dToAggregationExpressionTranslatorTests.cs | 85 +++++++++++++++---- 3 files changed, 71 insertions(+), 18 deletions(-) diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/MqlMethod.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/MqlMethod.cs index a1eae566efb..df10d07df08 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/MqlMethod.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/MqlMethod.cs @@ -83,7 +83,7 @@ static MqlMethod() __convertToDoubleFromBinDataWithOnErrorAndOnNull = ReflectionInfo.Method((BsonBinaryData field, string format, double? onError, double? onNull) => Mql.ConvertToDouble(field, format, onError, onNull)); __convertToIntFromBinData = ReflectionInfo.Method((BsonBinaryData field, string format) => Mql.ConvertToInt(field, format)); - __convertToIntFromBinDataWithOnErrorAndOnNull = ReflectionInfo.Method((BsonBinaryData field, string format, int? onError, int? onNull) => Mql.ToInt(field, format, onError, onNull)); + __convertToIntFromBinDataWithOnErrorAndOnNull = ReflectionInfo.Method((BsonBinaryData field, string format, int? onError, int? onNull) => Mql.ConvertToInt(field, format, onError, onNull)); __convertToLongFromBinData = ReflectionInfo.Method((BsonBinaryData field, string format) => Mql.ConvertToLong(field, format)); __convertToLongFromBinDataWithOnErrorAndOnNull = ReflectionInfo.Method((BsonBinaryData field, string format, long? onError, long? onNull) => Mql.ConvertToLong(field, format, onError, onNull)); diff --git a/src/MongoDB.Driver/Mql.cs b/src/MongoDB.Driver/Mql.cs index c0be30d74ba..0a2316f150b 100644 --- a/src/MongoDB.Driver/Mql.cs +++ b/src/MongoDB.Driver/Mql.cs @@ -312,7 +312,7 @@ public static string ConvertToString(BsonBinaryData field, string format, string /// /// /// - public static int? ToInt(BsonBinaryData field, string format, int? onError, int? onNull) + public static int? ConvertToInt(BsonBinaryData field, string format, int? onError, int? onNull) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs index 48c24f60d53..f0a412abd9e 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs @@ -21,7 +21,6 @@ using MongoDB.Bson.Serialization.Attributes; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.TestHelpers.XunitExtensions; -using MongoDB.Driver.Linq; using MongoDB.Driver.TestHelpers; using Xunit; @@ -39,7 +38,7 @@ public ConvertMethodToAggregationExpressionTranslatorTests(ClassFixture fixture) [Theory] [InlineData(3, -0.5, null)] [InlineData(2, null, "MongoCommandException")] - public void MongoDBFunctions_ConvertToDoubleFromBson_should_work(int id, double? expectedResult, string expectedException) + public void MongoDBFunctions_ConvertToDoubleFromBinData_should_work(int id, double? expectedResult, string expectedException) { RequireServer.Check().Supports(Feature.ConvertBinDataToFromNumeric); @@ -59,11 +58,11 @@ public void MongoDBFunctions_ConvertToDoubleFromBson_should_work(int id, double? } [Theory] - [InlineData(2, 15.0, 15.0, null)] - [InlineData(0, 12.0, null, 12.0)] - [InlineData(2, null, null, null)] - [InlineData(0, null, null, null)] - public void MongoDBFunctions_ConvertToDoubleFromBsonWithOnErrorAndOnNull_should_work(int id, double? expectedResult, double? onError, double? onNull) + [InlineData(2, 15.2, 15.2, 22.3)] + [InlineData(0, 22.3, 15.2, 22.3)] + [InlineData(2, null, null, 22.3)] + [InlineData(0, null, 15.2, null)] + public void MongoDBFunctions_ConvertToDoubleFromBinDataWithOnErrorAndOnNull_should_work(int id, double? expectedResult, double? onError, double? onNull) { RequireServer.Check().Supports(Feature.ConvertBinDataToFromNumeric); @@ -72,8 +71,8 @@ public void MongoDBFunctions_ConvertToDoubleFromBsonWithOnErrorAndOnNull_should_ .Where(x => x.Id == id) .Select(x => Mql.ConvertToDouble(x.BinaryProperty, "hex", onError, onNull)); - var onErrorString = onError == null ? "null" : onError.Value.ToString("F1", NumberFormatInfo.InvariantInfo); - var onNullString = onNull == null ? "null" : onNull.Value.ToString("F1", NumberFormatInfo.InvariantInfo); + var onErrorString = onError == null ? "null" : onError.Value.ToString(NumberFormatInfo.InvariantInfo); + var onNullString = onNull == null ? "null" : onNull.Value.ToString(NumberFormatInfo.InvariantInfo); var expectedStages = new[] { @@ -84,11 +83,59 @@ public void MongoDBFunctions_ConvertToDoubleFromBsonWithOnErrorAndOnNull_should_ AssertOutcome(collection, queryable, expectedStages, expectedResult); } + [Theory] + [InlineData(4, 2, null)] + [InlineData(2, null, "MongoCommandException")] + public void MongoDBFunctions_ConvertToIntFromBinData_should_work(int id, int? expectedResult, string expectedException) + { + RequireServer.Check().Supports(Feature.ConvertBinDataToFromNumeric); + + var collection = Fixture.Collection; + var queryable = collection.AsQueryable() + .Where(x => x.Id == id) + .Select(x => Mql.ConvertToInt(x.BinaryProperty, "hex")); + + var expectedStages = + new[] + { + $"{{ $match : {{ _id : {id} }} }}", + $"{{ $project: {{ _v : {{ $convert : {{ input : '$BinaryProperty', to : 16, format : 'hex' }} }}, _id : 0 }} }}", + }; + + AssertOutcome(collection, queryable, expectedStages, expectedResult, expectedException); + } + + [Theory] + [InlineData(2, 15, 15, 22)] + [InlineData(0, 22, 15, 22)] + [InlineData(2, null, null, 22)] + [InlineData(0, null, 15, null)] + public void MongoDBFunctions_ConvertToIntFromBinDataWithOnErrorAndOnNull_should_work(int id, int? expectedResult, int? onError, int? onNull) + { + RequireServer.Check().Supports(Feature.ConvertBinDataToFromNumeric); + + var collection = Fixture.Collection; + var queryable = collection.AsQueryable() + .Where(x => x.Id == id) + .Select(x => Mql.ConvertToInt(x.BinaryProperty, "hex", onError, onNull)); + + var onErrorString = onError == null ? "null" : onError.Value.ToString(NumberFormatInfo.InvariantInfo); + var onNullString = onNull == null ? "null" : onNull.Value.ToString(NumberFormatInfo.InvariantInfo); + var expectedStages = + new[] + { + $"{{ $match : {{ _id : {id} }} }}", + $"{{ $project: {{ _v : {{ $convert : {{ input : '$BinaryProperty', to : 16, onError: {onErrorString}, onNull: {onNullString}, format : 'hex' }} }}, _id : 0 }} }}", + }; + + AssertOutcome(collection, queryable, expectedStages, expectedResult); + } + [Theory] [InlineData(2, "867dee52-c331-484e-92d1-c56479b8e67e", null)] [InlineData(1, null, "MongoCommandException")] - public void MongoDBFunctions_ConvertToStringFromBson_should_work(int id, string expectedResult, string expectedException) + public void MongoDBFunctions_ConvertToStringFromBinData_should_work(int id, string expectedResult, string expectedException) { RequireServer.Check().Supports(Feature.ConvertBinDataToFromNumeric); @@ -108,22 +155,27 @@ public void MongoDBFunctions_ConvertToStringFromBson_should_work(int id, string } [Theory] - [InlineData(0, "onNull")] - [InlineData(1, "onError")] - public void MongoDBFunctions_ConvertToStringFromBsonWithOnErrorAndOnNull_should_work(int id, string expectedResult) + [InlineData(0, "onNull", "onError", "onNull")] + [InlineData(1, "onError", "onError", "onNull")] + [InlineData(0, null, "onError", null)] + [InlineData(1, null, null, "onNull")] + public void MongoDBFunctions_ConvertToStringFromBinDataWithOnErrorAndOnNull_should_work(int id, string expectedResult, string onError, string onNull) { RequireServer.Check().Supports(Feature.ConvertBinDataToFromNumeric); var collection = Fixture.Collection; var queryable = collection.AsQueryable() .Where(x => x.Id == id) - .Select(x => Mql.ConvertToString(x.BinaryProperty, "uuid", "onError", "onNull")); + .Select(x => Mql.ConvertToString(x.BinaryProperty, "uuid", onError, onNull)); + + var onErrorString = onError == null ? "null" : $"'{onError}'"; + var onNullString = onNull == null ? "null" : $"'{onNull}'"; var expectedStages = new[] { $"{{ $match : {{ _id : {id} }} }}", - """{"$project": { "_v" : { "$convert" : { "input" : "$BinaryProperty", "to" : 2, "onError": "onError", "onNull": "onNull", "format" : "uuid" } }, "_id" : 0 }}""", + $"{{ $project: {{ _v : {{ $convert : {{ input : '$BinaryProperty', to : 2, onError: {onErrorString}, onNull: {onNullString}, format : 'uuid' }} }}, _id : 0 }} }}", }; AssertOutcome(collection, queryable, expectedStages, expectedResult); @@ -160,7 +212,8 @@ public sealed class ClassFixture : MongoCollectionFixture new TestClass {Id = 0 }, new TestClass {Id = 1, BinaryProperty = new BsonBinaryData([0, 1, 2])}, new TestClass {Id = 2, BinaryProperty = new BsonBinaryData(Guid.Parse("867dee52-c331-484e-92d1-c56479b8e67e"), GuidRepresentation.Standard)}, - new TestClass {Id = 3, BinaryProperty = new BsonBinaryData(Convert.FromBase64String("AAAAAAAA4L8="))} + new TestClass {Id = 3, BinaryProperty = new BsonBinaryData(Convert.FromBase64String("AAAAAAAA4L8="))}, + new TestClass {Id = 4, BinaryProperty = new BsonBinaryData(Convert.FromBase64String("Ag=="))} ]; private IEnumerable InitialDataUnTyped { get; } = From 4007671164dc7b8fc9e7c38274c0b185aa36b208 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Tue, 18 Mar 2025 09:27:18 +0100 Subject: [PATCH 12/82] Added convertToLong tests --- ...dToAggregationExpressionTranslatorTests.cs | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs index f0a412abd9e..61b9c89ef5e 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs @@ -131,6 +131,53 @@ public void MongoDBFunctions_ConvertToIntFromBinDataWithOnErrorAndOnNull_should_ AssertOutcome(collection, queryable, expectedStages, expectedResult); } + [Theory] + [InlineData(4, (long)2, null)] + [InlineData(2, null, "MongoCommandException")] + public void MongoDBFunctions_ConvertToLongFromBinData_should_work(int id, long? expectedResult, string expectedException) + { + RequireServer.Check().Supports(Feature.ConvertBinDataToFromNumeric); + + var collection = Fixture.Collection; + var queryable = collection.AsQueryable() + .Where(x => x.Id == id) + .Select(x => Mql.ConvertToLong(x.BinaryProperty, "hex")); + + var expectedStages = + new[] + { + $"{{ $match : {{ _id : {id} }} }}", + $"{{ $project: {{ _v : {{ $convert : {{ input : '$BinaryProperty', to : 18, format : 'hex' }} }}, _id : 0 }} }}", + }; + + AssertOutcome(collection, queryable, expectedStages, expectedResult, expectedException); + } + + [Theory] + [InlineData(2, (long)15, (long)15, (long)22)] + [InlineData(0, (long)22, (long)15, (long)22)] + [InlineData(2, null, null, (long)22)] + [InlineData(0, null, (long)15, null)] + public void MongoDBFunctions_ConvertToLongFromBinDataWithOnErrorAndOnNull_should_work(int id, long? expectedResult, long? onError, long? onNull) + { + RequireServer.Check().Supports(Feature.ConvertBinDataToFromNumeric); + + var collection = Fixture.Collection; + var queryable = collection.AsQueryable() + .Where(x => x.Id == id) + .Select(x => Mql.ConvertToLong(x.BinaryProperty, "hex", onError, onNull)); + + var onErrorString = onError == null ? "null" : onError.Value.ToString(NumberFormatInfo.InvariantInfo); + var onNullString = onNull == null ? "null" : onNull.Value.ToString(NumberFormatInfo.InvariantInfo); + var expectedStages = + new[] + { + $"{{ $match : {{ _id : {id} }} }}", + $"{{ $project: {{ _v : {{ $convert : {{ input : '$BinaryProperty', to : 18, onError: {onErrorString}, onNull: {onNullString}, format : 'hex' }} }}, _id : 0 }} }}", + }; + + AssertOutcome(collection, queryable, expectedStages, expectedResult); + } [Theory] [InlineData(2, "867dee52-c331-484e-92d1-c56479b8e67e", null)] From 9b40d910b2877fe479b15af21f20327a416eea8a Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Tue, 18 Mar 2025 11:05:36 +0100 Subject: [PATCH 13/82] Small corrections --- .../Linq/Linq3Implementation/Reflection/MqlMethod.cs | 12 ++++++------ src/MongoDB.Driver/Mql.cs | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/MqlMethod.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/MqlMethod.cs index df10d07df08..cc96247c798 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/MqlMethod.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/MqlMethod.cs @@ -66,14 +66,14 @@ static MqlMethod() __isNullOrMissing = ReflectionInfo.Method((object field) => Mql.IsNullOrMissing(field)); // Convert methods - __convertToBinDataFromDouble = ReflectionInfo.Method((double field, BsonBinarySubType subType, string format) => Mql.ConvertToBinData(field, subType, format)); - __convertToBinDataFromDoubleWithOnErrorAndOnNull = ReflectionInfo.Method((double field, BsonBinarySubType subType, string format, BsonBinaryData onError, BsonBinaryData onNull) + __convertToBinDataFromDouble = ReflectionInfo.Method((double? field, BsonBinarySubType subType, string format) => Mql.ConvertToBinData(field, subType, format)); + __convertToBinDataFromDoubleWithOnErrorAndOnNull = ReflectionInfo.Method((double? field, BsonBinarySubType subType, string format, BsonBinaryData onError, BsonBinaryData onNull) => Mql.ConvertToBinData(field, subType, format, onError, onNull)); - __convertToBinDataFromInt = ReflectionInfo.Method((int field, BsonBinarySubType subType, string format) => Mql.ConvertToBinData(field, subType, format)); - __convertToBinDataFromIntWithOnErrorAndOnNull = ReflectionInfo.Method((int field, BsonBinarySubType subType, string format, BsonBinaryData onError, BsonBinaryData onNull) + __convertToBinDataFromInt = ReflectionInfo.Method((int? field, BsonBinarySubType subType, string format) => Mql.ConvertToBinData(field, subType, format)); + __convertToBinDataFromIntWithOnErrorAndOnNull = ReflectionInfo.Method((int? field, BsonBinarySubType subType, string format, BsonBinaryData onError, BsonBinaryData onNull) => Mql.ConvertToBinData(field, subType, format, onError, onNull)); - __convertToBinDataFromLong = ReflectionInfo.Method((long field, BsonBinarySubType subType, string format) => Mql.ConvertToBinData(field, subType, format)); - __convertToBinDataFromLongWithOnErrorAndOnNull = ReflectionInfo.Method((long field, BsonBinarySubType subType, string format, BsonBinaryData onError, BsonBinaryData onNull) + __convertToBinDataFromLong = ReflectionInfo.Method((long? field, BsonBinarySubType subType, string format) => Mql.ConvertToBinData(field, subType, format)); + __convertToBinDataFromLongWithOnErrorAndOnNull = ReflectionInfo.Method((long? field, BsonBinarySubType subType, string format, BsonBinaryData onError, BsonBinaryData onNull) => Mql.ConvertToBinData(field, subType, format, onError, onNull)); __convertToBinDataFromString = ReflectionInfo.Method((string field, BsonBinarySubType subType, string format) => Mql.ConvertToBinData(field, subType, format)); __convertToBinDataFromStringWithOnErrorAndOnNull = ReflectionInfo.Method((string field, BsonBinarySubType subType, string format, BsonBinaryData onError, BsonBinaryData onNull) diff --git a/src/MongoDB.Driver/Mql.cs b/src/MongoDB.Driver/Mql.cs index 0a2316f150b..d1cc813820f 100644 --- a/src/MongoDB.Driver/Mql.cs +++ b/src/MongoDB.Driver/Mql.cs @@ -188,7 +188,7 @@ public static BsonBinaryData ConvertToBinData(string field, BsonBinarySubType su /// /// /// - public static BsonBinaryData ConvertToBinData(int field, BsonBinarySubType subtype, string format) + public static BsonBinaryData ConvertToBinData(int? field, BsonBinarySubType subtype, string format) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } @@ -203,7 +203,7 @@ public static BsonBinaryData ConvertToBinData(int field, BsonBinarySubType subty /// /// /// - public static BsonBinaryData ConvertToBinData(int field, BsonBinarySubType subtype, string format, + public static BsonBinaryData ConvertToBinData(int? field, BsonBinarySubType subtype, string format, BsonBinaryData onError, BsonBinaryData onNull) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); @@ -216,7 +216,7 @@ public static BsonBinaryData ConvertToBinData(int field, BsonBinarySubType subty /// /// /// - public static BsonBinaryData ConvertToBinData(long field, BsonBinarySubType subtype, string format) + public static BsonBinaryData ConvertToBinData(long? field, BsonBinarySubType subtype, string format) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } @@ -231,7 +231,7 @@ public static BsonBinaryData ConvertToBinData(long field, BsonBinarySubType subt /// /// /// - public static BsonBinaryData ConvertToBinData(long field, BsonBinarySubType subtype, string format, + public static BsonBinaryData ConvertToBinData(long? field, BsonBinarySubType subtype, string format, BsonBinaryData onError, BsonBinaryData onNull) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); @@ -244,7 +244,7 @@ public static BsonBinaryData ConvertToBinData(long field, BsonBinarySubType subt /// /// /// - public static BsonBinaryData ConvertToBinData(double field, BsonBinarySubType subtype, string format) + public static BsonBinaryData ConvertToBinData(double? field, BsonBinarySubType subtype, string format) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } @@ -259,7 +259,7 @@ public static BsonBinaryData ConvertToBinData(double field, BsonBinarySubType su /// /// /// - public static BsonBinaryData ConvertToBinData(double field, BsonBinarySubType subtype, string format, + public static BsonBinaryData ConvertToBinData(double? field, BsonBinarySubType subtype, string format, BsonBinaryData onError, BsonBinaryData onNull) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); From 9e6c11a7b7fc7dda536de5763adfb149db57f7ee Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Tue, 18 Mar 2025 11:09:11 +0100 Subject: [PATCH 14/82] Various fixes --- ...essionToAggregationExpressionTranslator.cs | 1 - ...MethodToAggregationExpressionTranslator.cs | 7 +- ...dToAggregationExpressionTranslatorTests.cs | 71 ++++++++++++++++++- 3 files changed, 73 insertions(+), 6 deletions(-) diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/ConstantExpressionToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/ConstantExpressionToAggregationExpressionTranslator.cs index 7487627213d..6b9ca5b0a8c 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/ConstantExpressionToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/ConstantExpressionToAggregationExpressionTranslator.cs @@ -14,7 +14,6 @@ */ using System.Linq.Expressions; -using MongoDB.Bson.IO; using MongoDB.Bson.Serialization; using MongoDB.Driver.Linq.Linq3Implementation.Ast.Expressions; using MongoDB.Driver.Linq.Linq3Implementation.Serializers; diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs index a1465c3b0bc..be907e0be78 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs @@ -7,7 +7,6 @@ using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Serializers; using MongoDB.Driver.Linq.Linq3Implementation.Ast.Expressions; -using MongoDB.Driver.Linq.Linq3Implementation.ExtensionMethods; using MongoDB.Driver.Linq.Linq3Implementation.Reflection; namespace MongoDB.Driver.Linq.Linq3Implementation.Translators.ExpressionToAggregationExpressionTranslators.MethodTranslators @@ -16,8 +15,10 @@ internal class ConvertMethodToAggregationExpressionTranslator { private static readonly List<(MethodInfo[] Methods, IBsonSerializer Serializer, BsonType Type, int? FormatIndex, int? SubTypeIndex, int? OnErrorIndex, int? OnNullIndex)> _methodMappings = [ - ([MqlMethod.ConvertToBinDataFromString, MqlMethod.ConvertToBinDataFromInt, MqlMethod.ConvertToBinDataFromLong, MqlMethod.ConvertToBinDataFromDouble], BsonBinaryDataSerializer.Instance, BsonType.Binary, 2, 1, null, null), - ([MqlMethod.ConvertToBinDataFromStringWithOnErrorAndOnNull, MqlMethod.ConvertToBinDataFromIntWithOnErrorAndOnNull, MqlMethod.ConvertToBinDataFromLongWithOnErrorAndOnNull, MqlMethod.ConvertToBinDataFromDoubleWithOnErrorAndOnNull], BsonBinaryDataSerializer.Instance, BsonType.Binary, 2, 1, 3, 4), + ([MqlMethod.ConvertToBinDataFromString, MqlMethod.ConvertToBinDataFromInt, MqlMethod.ConvertToBinDataFromLong, MqlMethod.ConvertToBinDataFromDouble], + BsonBinaryDataSerializer.Instance, BsonType.Binary, 2, 1, null, null), + ([MqlMethod.ConvertToBinDataFromStringWithOnErrorAndOnNull, MqlMethod.ConvertToBinDataFromIntWithOnErrorAndOnNull, MqlMethod.ConvertToBinDataFromLongWithOnErrorAndOnNull, MqlMethod.ConvertToBinDataFromDoubleWithOnErrorAndOnNull], + BsonBinaryDataSerializer.Instance, BsonType.Binary, 2, 1, 3, 4), ([MqlMethod.ConvertToStringFromBinData], StringSerializer.Instance, BsonType.String, 1, null, null, null), ([MqlMethod.ConvertToStringFromBinDataWithOnErrorAndOnNull], StringSerializer.Instance, BsonType.String, 1, null, 2, 3), ([MqlMethod.ConvertToIntFromBinData], new NullableSerializer(Int32Serializer.Instance), BsonType.Int32, 1, null, null, null), diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs index 61b9c89ef5e..141da3c9b94 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs @@ -35,6 +35,66 @@ public ConvertMethodToAggregationExpressionTranslatorTests(ClassFixture fixture) { } + // To BinData + + [Theory] + [InlineData(3, "AAAAAAAA4L8=", null)] + [InlineData(1, null, "FormatException")] + public void MongoDBFunctions_ConvertToBinDataFromDouble_should_work(int id, string expectedBase64, string expectedException) + { + RequireServer.Check().Supports(Feature.ConvertBinDataToFromNumeric); + + var collection = Fixture.Collection; + var queryable = collection.AsQueryable() + .Where(x => x.Id == id) + .Select(x => Mql.ConvertToBinData(x.DoubleProperty, BsonBinarySubType.Binary, "hex")); + + var expectedStages = + new[] + { + $"{{ $match : {{ _id : {id} }} }}", + $"{{ $project: {{ _v : {{ $convert : {{ input : '$DoubleProperty', to : {{ type: 5, subtype: 0 }}, format : 'hex' }} }}, _id : 0 }} }}", + }; + + var expectedResult = expectedBase64 is null? null : new BsonBinaryData(Convert.FromBase64String(expectedBase64)); + AssertOutcome(collection, queryable, expectedStages, expectedResult, expectedException); + } + + [Theory] + [InlineData(2, "AAAAAAAA4L8=", "AAAAAAAA4L8=", "AAAAAAAABMA=")] + [InlineData(0, "AAAAAAAABMA=", "AAAAAAAA4L8=", "AAAAAAAABMA=")] + [InlineData(2, null, null, "AAAAAAAABMA=")] + [InlineData(0, null, "AAAAAAAA4L8=", null)] + public void MongoDBFunctions_ConvertToBinDataFromDoubleWithOnErrorAndOnNull_should_work(int id, string expectedBase64, string onErrorBase64, string onNullBase64) + { + RequireServer.Check().Supports(Feature.ConvertBinDataToFromNumeric); + + //TODO The issue here is that BsonBinaryDataSerializer can't serialize null values, an exception is thrown (because it's a BsonValueSerializerBase) + //TODO Maybe we should use BsonValueCSharpNullSerializer? + + var onErrorBinData = onErrorBase64 == null ? null : new BsonBinaryData(Convert.FromBase64String(onErrorBase64)); + var onNullBinData = onNullBase64 == null ? null : new BsonBinaryData(Convert.FromBase64String(onNullBase64)); + + var collection = Fixture.Collection; + var queryable = collection.AsQueryable() + .Where(x => x.Id == id) + .Select(x => Mql.ConvertToBinData(x.DoubleProperty, BsonBinarySubType.Function, "base64", onErrorBinData, onNullBinData)); + + var onErrorString = onErrorBase64 == null ? "null" : $"BinData(0, '{onErrorBase64}')"; + var onNullString = onNullBase64 == null ? "null" : $"BinData(0, '{onNullBase64}')"; + var expectedStages = + new[] + { + $"{{ $match : {{ _id : {id} }} }}", + $"{{ $project: {{ _v : {{ $convert : {{ input : '$DoubleProperty', to : {{ type: 5, subtype: 1 }}, onError: {onErrorString}, onNull: {onNullString}, format : 'base64' }} }}, _id : 0 }} }}", + }; + + var expectedResult = expectedBase64 is null? null : new BsonBinaryData(Convert.FromBase64String(expectedBase64)); + AssertOutcome(collection, queryable, expectedStages, expectedResult); + } + + // To Double + [Theory] [InlineData(3, -0.5, null)] [InlineData(2, null, "MongoCommandException")] @@ -83,6 +143,8 @@ public void MongoDBFunctions_ConvertToDoubleFromBinDataWithOnErrorAndOnNull_shou AssertOutcome(collection, queryable, expectedStages, expectedResult); } + // To Int + [Theory] [InlineData(4, 2, null)] [InlineData(2, null, "MongoCommandException")] @@ -131,6 +193,8 @@ public void MongoDBFunctions_ConvertToIntFromBinDataWithOnErrorAndOnNull_should_ AssertOutcome(collection, queryable, expectedStages, expectedResult); } + // To Long + [Theory] [InlineData(4, (long)2, null)] [InlineData(2, null, "MongoCommandException")] @@ -179,6 +243,8 @@ public void MongoDBFunctions_ConvertToLongFromBinDataWithOnErrorAndOnNull_should AssertOutcome(collection, queryable, expectedStages, expectedResult); } + // To String + [Theory] [InlineData(2, "867dee52-c331-484e-92d1-c56479b8e67e", null)] [InlineData(1, null, "MongoCommandException")] @@ -247,6 +313,7 @@ private void AssertOutcome(IMongoCollection collection, } else { + Assert.NotNull(exception); Assert.Equal(expectedException, exception.GetType().Name); } } @@ -258,8 +325,8 @@ public sealed class ClassFixture : MongoCollectionFixture [ new TestClass {Id = 0 }, new TestClass {Id = 1, BinaryProperty = new BsonBinaryData([0, 1, 2])}, - new TestClass {Id = 2, BinaryProperty = new BsonBinaryData(Guid.Parse("867dee52-c331-484e-92d1-c56479b8e67e"), GuidRepresentation.Standard)}, - new TestClass {Id = 3, BinaryProperty = new BsonBinaryData(Convert.FromBase64String("AAAAAAAA4L8="))}, + new TestClass {Id = 2, BinaryProperty = new BsonBinaryData(Guid.Parse("867dee52-c331-484e-92d1-c56479b8e67e"), GuidRepresentation.Standard), DoubleProperty = 2.45673345}, + new TestClass {Id = 3, BinaryProperty = new BsonBinaryData(Convert.FromBase64String("AAAAAAAA4L8=")), DoubleProperty = -0.5}, new TestClass {Id = 4, BinaryProperty = new BsonBinaryData(Convert.FromBase64String("Ag=="))} ]; From f1cc942ad108192d17b433b93cb879771db919f8 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Tue, 18 Mar 2025 11:14:52 +0100 Subject: [PATCH 15/82] Small corrections --- ...dToAggregationExpressionTranslatorTests.cs | 27 +++---------------- 1 file changed, 3 insertions(+), 24 deletions(-) diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs index 141da3c9b94..3950ac7df8b 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs @@ -62,16 +62,13 @@ public void MongoDBFunctions_ConvertToBinDataFromDouble_should_work(int id, stri [Theory] [InlineData(2, "AAAAAAAA4L8=", "AAAAAAAA4L8=", "AAAAAAAABMA=")] - [InlineData(0, "AAAAAAAABMA=", "AAAAAAAA4L8=", "AAAAAAAABMA=")] - [InlineData(2, null, null, "AAAAAAAABMA=")] - [InlineData(0, null, "AAAAAAAA4L8=", null)] + // [InlineData(0, "AAAAAAAABMA=", "AAAAAAAA4L8=", "AAAAAAAABMA=")] //TODO Don't know how to cause an error + // [InlineData(2, null, null, "AAAAAAAABMA=")] //TODO The issue here is that BsonBinaryDataSerializer can't serialize null values, an exception is thrown (because it's a BsonValueSerializerBase) + // [InlineData(0, null, "AAAAAAAA4L8=", null)] public void MongoDBFunctions_ConvertToBinDataFromDoubleWithOnErrorAndOnNull_should_work(int id, string expectedBase64, string onErrorBase64, string onNullBase64) { RequireServer.Check().Supports(Feature.ConvertBinDataToFromNumeric); - //TODO The issue here is that BsonBinaryDataSerializer can't serialize null values, an exception is thrown (because it's a BsonValueSerializerBase) - //TODO Maybe we should use BsonValueCSharpNullSerializer? - var onErrorBinData = onErrorBase64 == null ? null : new BsonBinaryData(Convert.FromBase64String(onErrorBase64)); var onNullBinData = onNullBase64 == null ? null : new BsonBinaryData(Convert.FromBase64String(onNullBase64)); @@ -329,29 +326,11 @@ public sealed class ClassFixture : MongoCollectionFixture new TestClass {Id = 3, BinaryProperty = new BsonBinaryData(Convert.FromBase64String("AAAAAAAA4L8=")), DoubleProperty = -0.5}, new TestClass {Id = 4, BinaryProperty = new BsonBinaryData(Convert.FromBase64String("Ag=="))} ]; - - private IEnumerable InitialDataUnTyped { get; } = - [ - BsonDocument.Parse("{ _id : 7 }") - ]; - - - //TODO Remove all of this - protected override void InitializeTestCase() - { - base.InitializeTestCase(); - // var collection = Database.GetCollection(Collection.CollectionNamespace.CollectionName); - // collection.InsertMany(InitialDataUnTyped); - } - - public IMongoCollection UnTypedCollection => - Database.GetCollection(Collection.CollectionNamespace.CollectionName); } public class TestClass { public int Id { get; set; } - [BsonIgnoreIfDefault] public BsonBinaryData BinaryProperty { get; set; } public double? DoubleProperty { get; set; } From 6b3fb548614aafc3d8bc579dbb6d82029c5879f2 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Tue, 18 Mar 2025 11:21:06 +0100 Subject: [PATCH 16/82] Fixed docs --- src/MongoDB.Driver/Mql.cs | 304 +++++++++++++++++++------------------- 1 file changed, 152 insertions(+), 152 deletions(-) diff --git a/src/MongoDB.Driver/Mql.cs b/src/MongoDB.Driver/Mql.cs index d1cc813820f..fb1785fcf52 100644 --- a/src/MongoDB.Driver/Mql.cs +++ b/src/MongoDB.Driver/Mql.cs @@ -49,106 +49,114 @@ public static TValue Constant(TValue value, BsonType representaion) throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } + /// - /// Converts a string to a DateTime using the $dateFromString aggregation operator. + /// Converts a string field to a BsonBinaryData using the $convert aggregation operator. /// - /// The string. - /// A DateTime. - public static DateTime DateFromString(string dateString) + /// The field. + /// The BsonBinaryData subtype of the result value. + /// The format string. + /// The converted field. + public static BsonBinaryData ConvertToBinData(string field, BsonBinarySubType subtype, string format) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } /// - /// Converts a string to a DateTime using the $dateFromString aggregation operator. + /// /// - /// The string. + /// The field. + /// The BsonBinaryData subtype of the result value. /// The format string. - /// A DateTime. - public static DateTime DateFromString( - string dateString, - string format) + /// The onError value. + /// The onNull value. + /// The converted field. + public static BsonBinaryData ConvertToBinData(string field, BsonBinarySubType subtype, string format, + BsonBinaryData onError, BsonBinaryData onNull) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } /// - /// Converts a string to a DateTime using the $dateFromString aggregation operator. + /// /// - /// The string. + /// The field. + /// The BsonBinaryData subtype of the result value. /// The format string. - /// The time zone. - /// A DateTime. - public static DateTime DateFromString( - string dateString, - string format, - string timezone) + /// The converted field. + public static BsonBinaryData ConvertToBinData(int? field, BsonBinarySubType subtype, string format) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } /// - /// Converts a string to a DateTime using the $dateFromString aggregation operator. + /// /// - /// The string. + /// The field. + /// The BsonBinaryData subtype of the result value. /// The format string. - /// The time zone. /// The onError value. /// The onNull value. - /// A DateTime. - public static DateTime? DateFromString( - string dateString, - string format, - string timezone, - DateTime? onError, - DateTime? onNull) + /// The converted field. + /// + public static BsonBinaryData ConvertToBinData(int? field, BsonBinarySubType subtype, string format, + BsonBinaryData onError, BsonBinaryData onNull) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } /// - /// Tests whether a field exists. + /// /// - /// The type of the field. /// The field. - /// true if the field exists. - public static bool Exists(TField field) + /// The BsonBinaryData subtype of the result value. + /// The format string. + /// The converted field. + public static BsonBinaryData ConvertToBinData(long? field, BsonBinarySubType subtype, string format) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } /// - /// Gets the value of a field in a document. + /// /// - /// The type of the document. - /// The type of the field. - /// The document. - /// The field name. - /// The field serializer. - /// The value of the field. - public static TField Field(TDocument document, string fieldName, IBsonSerializer fieldSerializer) + /// The field. + /// The BsonBinaryData subtype of the result value. + /// The format string. + /// The onError value. + /// The onNull value. + /// The converted field. + /// + public static BsonBinaryData ConvertToBinData(long? field, BsonBinarySubType subtype, string format, + BsonBinaryData onError, BsonBinaryData onNull) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } /// - /// Tests whether a field is missing. + /// /// - /// The type of the field. /// The field. - /// true if the field is missing. - public static bool IsMissing(TField field) + /// The BsonBinaryData subtype of the result value. + /// The format string. + /// The converted field. + public static BsonBinaryData ConvertToBinData(double? field, BsonBinarySubType subtype, string format) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } /// - /// Tests whether a field is null or missing. + /// /// - /// The type of the field. /// The field. - /// true if the field is null or missing. - public static bool IsNullOrMissing(TField field) + /// The BsonBinaryData subtype of the result value. + /// The format string. + /// The onError value. + /// The onNull value. + /// The converted field. + /// + public static BsonBinaryData ConvertToBinData(double? field, BsonBinarySubType subtype, string format, + BsonBinaryData onError, BsonBinaryData onNull) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } @@ -156,11 +164,11 @@ public static bool IsNullOrMissing(TField field) /// /// /// - /// - /// - /// - /// - public static BsonBinaryData ConvertToBinData(string field, BsonBinarySubType subtype, string format) + /// The field. + /// The format string. + /// The converted field. + /// + public static string ConvertToString(BsonBinaryData field, string format) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } @@ -168,15 +176,13 @@ public static BsonBinaryData ConvertToBinData(string field, BsonBinarySubType su /// /// /// - /// - /// - /// - /// + /// The field. + /// The format string. /// - /// + /// + /// The converted field. /// - public static BsonBinaryData ConvertToBinData(string field, BsonBinarySubType subtype, string format, - BsonBinaryData onError, BsonBinaryData onNull) + public static string ConvertToString(BsonBinaryData field, string format, string onError, string onNull) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } @@ -184,11 +190,11 @@ public static BsonBinaryData ConvertToBinData(string field, BsonBinarySubType su /// /// /// - /// - /// - /// - /// - public static BsonBinaryData ConvertToBinData(int? field, BsonBinarySubType subtype, string format) + /// The field. + /// The format string. + /// The converted field. + /// + public static int? ConvertToInt(BsonBinaryData field, string format) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } @@ -196,15 +202,13 @@ public static BsonBinaryData ConvertToBinData(int? field, BsonBinarySubType subt /// /// /// - /// - /// - /// - /// + /// The field. + /// The format string. /// - /// + /// + /// The converted field. /// - public static BsonBinaryData ConvertToBinData(int? field, BsonBinarySubType subtype, string format, - BsonBinaryData onError, BsonBinaryData onNull) + public static int? ConvertToInt(BsonBinaryData field, string format, int? onError, int? onNull) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } @@ -212,11 +216,11 @@ public static BsonBinaryData ConvertToBinData(int? field, BsonBinarySubType subt /// /// /// - /// - /// - /// - /// - public static BsonBinaryData ConvertToBinData(long? field, BsonBinarySubType subtype, string format) + /// The field. + /// The format string. + /// The converted field. + /// + public static long? ConvertToLong(BsonBinaryData field, string format) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } @@ -224,15 +228,13 @@ public static BsonBinaryData ConvertToBinData(long? field, BsonBinarySubType sub /// /// /// - /// - /// - /// - /// + /// The field. + /// The format string. /// - /// + /// + /// The converted field. /// - public static BsonBinaryData ConvertToBinData(long? field, BsonBinarySubType subtype, string format, - BsonBinaryData onError, BsonBinaryData onNull) + public static long? ConvertToLong(BsonBinaryData field, string format, long? onError, long? onNull) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } @@ -240,11 +242,11 @@ public static BsonBinaryData ConvertToBinData(long? field, BsonBinarySubType sub /// /// /// - /// - /// - /// - /// - public static BsonBinaryData ConvertToBinData(double? field, BsonBinarySubType subtype, string format) + /// The field. + /// The format string. + /// The converted field. + /// + public static double? ConvertToDouble(BsonBinaryData field, string format) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } @@ -252,119 +254,117 @@ public static BsonBinaryData ConvertToBinData(double? field, BsonBinarySubType s /// /// /// - /// - /// - /// - /// + /// The field. + /// The format string. /// - /// + /// + /// The converted field. /// - public static BsonBinaryData ConvertToBinData(double? field, BsonBinarySubType subtype, string format, - BsonBinaryData onError, BsonBinaryData onNull) + public static double? ConvertToDouble(BsonBinaryData field, string format, double? onError, double? onNull) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } /// - /// + /// Converts a string to a DateTime using the $dateFromString aggregation operator. /// - /// - /// - /// - /// - public static string ConvertToString(BsonBinaryData field, string format) + /// The string. + /// A DateTime. + public static DateTime DateFromString(string dateString) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } /// - /// + /// Converts a string to a DateTime using the $dateFromString aggregation operator. /// - /// - /// - /// - /// - /// - /// - public static string ConvertToString(BsonBinaryData field, string format, string onError, string onNull) + /// The string. + /// The format string. + /// A DateTime. + public static DateTime DateFromString( + string dateString, + string format) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } /// - /// + /// Converts a string to a DateTime using the $dateFromString aggregation operator. /// - /// - /// - /// - /// - public static int? ConvertToInt(BsonBinaryData field, string format) + /// The string. + /// The format string. + /// The time zone. + /// A DateTime. + public static DateTime DateFromString( + string dateString, + string format, + string timezone) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } /// - /// + /// Converts a string to a DateTime using the $dateFromString aggregation operator. /// - /// - /// - /// - /// - /// - /// - public static int? ConvertToInt(BsonBinaryData field, string format, int? onError, int? onNull) + /// The string. + /// The format string. + /// The time zone. + /// The onError value. + /// The onNull value. + /// A DateTime. + public static DateTime? DateFromString( + string dateString, + string format, + string timezone, + DateTime? onError, + DateTime? onNull) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } /// - /// + /// Tests whether a field exists. /// - /// - /// - /// - /// - public static long? ConvertToLong(BsonBinaryData field, string format) + /// The type of the field. + /// The field. + /// true if the field exists. + public static bool Exists(TField field) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } /// - /// + /// Gets the value of a field in a document. /// - /// - /// - /// - /// - /// - /// - public static long? ConvertToLong(BsonBinaryData field, string format, long? onError, long? onNull) + /// The type of the document. + /// The type of the field. + /// The document. + /// The field name. + /// The field serializer. + /// The value of the field. + public static TField Field(TDocument document, string fieldName, IBsonSerializer fieldSerializer) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } /// - /// + /// Tests whether a field is missing. /// - /// - /// - /// - /// - public static double? ConvertToDouble(BsonBinaryData field, string format) + /// The type of the field. + /// The field. + /// true if the field is missing. + public static bool IsMissing(TField field) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } /// - /// + /// Tests whether a field is null or missing. /// - /// - /// - /// - /// - /// - /// - public static double? ConvertToDouble(BsonBinaryData field, string format, double? onError, double? onNull) + /// The type of the field. + /// The field. + /// true if the field is null or missing. + public static bool IsNullOrMissing(TField field) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } From f25e15b920f225e81693cc4978a5e16ef4a2e4c9 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Tue, 18 Mar 2025 11:28:08 +0100 Subject: [PATCH 17/82] Fixed docs --- src/MongoDB.Driver/Mql.cs | 152 ++++++++++++++++++-------------------- 1 file changed, 72 insertions(+), 80 deletions(-) diff --git a/src/MongoDB.Driver/Mql.cs b/src/MongoDB.Driver/Mql.cs index fb1785fcf52..de657a01ad5 100644 --- a/src/MongoDB.Driver/Mql.cs +++ b/src/MongoDB.Driver/Mql.cs @@ -51,216 +51,208 @@ public static TValue Constant(TValue value, BsonType representaion) /// - /// Converts a string field to a BsonBinaryData using the $convert aggregation operator. + /// Converts a string to a BsonBinaryData using the $convert aggregation operator. /// - /// The field. + /// The value. /// The BsonBinaryData subtype of the result value. /// The format string. - /// The converted field. - public static BsonBinaryData ConvertToBinData(string field, BsonBinarySubType subtype, string format) + /// The converted value. + public static BsonBinaryData ConvertToBinData(string value, BsonBinarySubType subtype, string format) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } /// - /// + /// Converts a string to a BsonBinaryData using the $convert aggregation operator. /// - /// The field. + /// The value. /// The BsonBinaryData subtype of the result value. /// The format string. /// The onError value. /// The onNull value. - /// The converted field. - public static BsonBinaryData ConvertToBinData(string field, BsonBinarySubType subtype, string format, + /// The converted value. + public static BsonBinaryData ConvertToBinData(string value, BsonBinarySubType subtype, string format, BsonBinaryData onError, BsonBinaryData onNull) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } /// - /// + /// Converts an int to a BsonBinaryData using the $convert aggregation operator. /// - /// The field. + /// The value. /// The BsonBinaryData subtype of the result value. /// The format string. - /// The converted field. - public static BsonBinaryData ConvertToBinData(int? field, BsonBinarySubType subtype, string format) + /// The converted value. + public static BsonBinaryData ConvertToBinData(int? value, BsonBinarySubType subtype, string format) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } /// - /// + /// Converts an int? to a BsonBinaryData using the $convert aggregation operator. /// - /// The field. + /// The value. /// The BsonBinaryData subtype of the result value. /// The format string. /// The onError value. /// The onNull value. - /// The converted field. + /// The converted value. /// - public static BsonBinaryData ConvertToBinData(int? field, BsonBinarySubType subtype, string format, + public static BsonBinaryData ConvertToBinData(int? value, BsonBinarySubType subtype, string format, BsonBinaryData onError, BsonBinaryData onNull) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } /// - /// + /// Converts a long? to a BsonBinaryData using the $convert aggregation operator. /// - /// The field. + /// The value. /// The BsonBinaryData subtype of the result value. /// The format string. - /// The converted field. - public static BsonBinaryData ConvertToBinData(long? field, BsonBinarySubType subtype, string format) + /// The converted value. + public static BsonBinaryData ConvertToBinData(long? value, BsonBinarySubType subtype, string format) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } /// - /// + /// Converts a long? to a BsonBinaryData using the $convert aggregation operator. /// - /// The field. + /// The value. /// The BsonBinaryData subtype of the result value. /// The format string. /// The onError value. /// The onNull value. - /// The converted field. + /// The converted value. /// - public static BsonBinaryData ConvertToBinData(long? field, BsonBinarySubType subtype, string format, + public static BsonBinaryData ConvertToBinData(long? value, BsonBinarySubType subtype, string format, BsonBinaryData onError, BsonBinaryData onNull) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } /// - /// + /// Converts a double? to a BsonBinaryData using the $convert aggregation operator. /// - /// The field. + /// The value. /// The BsonBinaryData subtype of the result value. /// The format string. - /// The converted field. - public static BsonBinaryData ConvertToBinData(double? field, BsonBinarySubType subtype, string format) + /// The converted value. + public static BsonBinaryData ConvertToBinData(double? value, BsonBinarySubType subtype, string format) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } /// - /// + /// Converts a double? to a BsonBinaryData using the $convert aggregation operator. /// - /// The field. + /// The field. /// The BsonBinaryData subtype of the result value. /// The format string. /// The onError value. /// The onNull value. - /// The converted field. + /// The converted value. /// - public static BsonBinaryData ConvertToBinData(double? field, BsonBinarySubType subtype, string format, + public static BsonBinaryData ConvertToBinData(double? value, BsonBinarySubType subtype, string format, BsonBinaryData onError, BsonBinaryData onNull) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } /// - /// + /// Converts a BsonBinaryData to a string using the $convert aggregation operator. /// - /// The field. + /// The value. /// The format string. - /// The converted field. - /// - public static string ConvertToString(BsonBinaryData field, string format) + /// The converted value. + public static string ConvertToString(BsonBinaryData value, string format) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } /// - /// + /// Converts a BsonBinaryData to a string using the $convert aggregation operator. /// - /// The field. + /// The value. /// The format string. - /// - /// - /// The converted field. - /// - public static string ConvertToString(BsonBinaryData field, string format, string onError, string onNull) + /// The onError value. + /// The onNull value. + /// The converted value. + public static string ConvertToString(BsonBinaryData value, string format, string onError, string onNull) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } /// - /// + /// Converts a BsonBinaryData to int? using the $convert aggregation operator. /// - /// The field. + /// The value. /// The format string. - /// The converted field. - /// - public static int? ConvertToInt(BsonBinaryData field, string format) + /// The converted value. + public static int? ConvertToInt(BsonBinaryData value, string format) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } /// - /// + /// Converts a BsonBinaryData to int? using the $convert aggregation operator. /// - /// The field. + /// The value. /// The format string. - /// - /// - /// The converted field. - /// - public static int? ConvertToInt(BsonBinaryData field, string format, int? onError, int? onNull) + /// The onError value. + /// The onNull value. + /// The converted value. + public static int? ConvertToInt(BsonBinaryData value, string format, int? onError, int? onNull) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } /// - /// + /// Converts a BsonBinaryData to long? using the $convert aggregation operator. /// - /// The field. + /// The value. /// The format string. - /// The converted field. - /// - public static long? ConvertToLong(BsonBinaryData field, string format) + /// The converted value. + public static long? ConvertToLong(BsonBinaryData value, string format) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } /// - /// + /// Converts a BsonBinaryData to long? using the $convert aggregation operator. /// - /// The field. + /// The value. /// The format string. - /// - /// - /// The converted field. - /// - public static long? ConvertToLong(BsonBinaryData field, string format, long? onError, long? onNull) + /// The onError value. + /// The onNull value. + /// The converted value. + public static long? ConvertToLong(BsonBinaryData value, string format, long? onError, long? onNull) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } /// - /// + /// Converts a BsonBinaryData to double? using the $convert aggregation operator. /// - /// The field. + /// The value. /// The format string. - /// The converted field. - /// - public static double? ConvertToDouble(BsonBinaryData field, string format) + /// The converted value. + public static double? ConvertToDouble(BsonBinaryData value, string format) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } /// - /// + /// Converts a BsonBinaryData to string using the $convert aggregation operator. /// - /// The field. + /// The value. /// The format string. - /// - /// - /// The converted field. - /// - public static double? ConvertToDouble(BsonBinaryData field, string format, double? onError, double? onNull) + /// The onError value. + /// The onNull value. + /// The converted value. + public static double? ConvertToDouble(BsonBinaryData value, string format, double? onError, double? onNull) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } From 73fe7004e3f6dc4c5fb54f690b0bfb52e2a41700 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Fri, 21 Mar 2025 17:34:49 +0100 Subject: [PATCH 18/82] Added byteOrder, removed format where not necessary --- .../Ast/Expressions/AstConvertExpression.cs | 31 +++++-- .../Ast/Expressions/AstExpression.cs | 6 +- .../Ast/Visitors/AstNodeVisitor.cs | 2 +- .../Reflection/MqlMethod.cs | 30 +++---- ...MethodToAggregationExpressionTranslator.cs | 86 +++++++++++++++---- src/MongoDB.Driver/Mql.cs | 64 ++++++++------ ...dToAggregationExpressionTranslatorTests.cs | 41 ++++----- 7 files changed, 174 insertions(+), 86 deletions(-) diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstConvertExpression.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstConvertExpression.cs index 9a0f19b515d..b33217e2b27 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstConvertExpression.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstConvertExpression.cs @@ -13,6 +13,7 @@ * limitations under the License. */ +using System; using MongoDB.Bson; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Linq.Linq3Implementation.Ast.Visitors; @@ -22,11 +23,12 @@ namespace MongoDB.Driver.Linq.Linq3Implementation.Ast.Expressions internal sealed class AstConvertExpression : AstExpression { private readonly AstExpression _input; - private readonly AstExpression _format; + private readonly string _format; private readonly AstExpression _onError; private readonly AstExpression _onNull; private readonly AstExpression _to; private readonly AstExpression _subType; + private readonly Mql.ByteOrder? _byteOrder; public AstConvertExpression( AstExpression input, @@ -34,7 +36,8 @@ public AstConvertExpression( AstExpression onError = null, AstExpression onNull = null, AstExpression subType = null, - AstExpression format = null) + string format = null, + Mql.ByteOrder? byteOrder = null) { _input = Ensure.IsNotNull(input, nameof(input)); _to = Ensure.IsNotNull(to, nameof(to)); @@ -42,10 +45,12 @@ public AstConvertExpression( _onNull = onNull; _subType = subType; _format = format; + _byteOrder = byteOrder; } + public Mql.ByteOrder? ByteOrder => _byteOrder; public AstExpression Input => _input; - public AstExpression Format => _format; + public string Format => _format; public override AstNodeType NodeType => AstNodeType.ConvertExpression; public AstExpression OnError => _onError; public AstExpression OnNull => _onNull; @@ -73,7 +78,8 @@ public override BsonValue Render() }, { "onError", () => _onError.Render(), _onError != null }, { "onNull", () => _onNull.Render(), _onNull != null }, - { "format", () => _format.Render(), _format != null} + { "format", () => _format, _format != null}, + { "byteOrder", () => MapMqlByteOrderToString(_byteOrder!.Value), _byteOrder != null} } } }; @@ -85,15 +91,26 @@ public AstConvertExpression Update( AstExpression onError, AstExpression onNull, AstExpression subType, - AstExpression format) + string format, + Mql.ByteOrder? byteOrder) { if (input == _input && to == _to && onError == _onError && onNull == _onNull && - subType == _subType && format == _format) + subType == _subType && format == _format && byteOrder == _byteOrder) { return this; } - return new AstConvertExpression(input, to, onError, onNull, subType, format); + return new AstConvertExpression(input, to, onError, onNull, subType, format, byteOrder); + } + + private static string MapMqlByteOrderToString(Mql.ByteOrder byteOrder) + { + return byteOrder switch + { + Mql.ByteOrder.BigEndian => "big", + Mql.ByteOrder.LittleEndian => "little", + _ => throw new ArgumentException($"Unexpected Mql.ByteOrder: {byteOrder}.", nameof(byteOrder)) + }; } } } diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstExpression.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstExpression.cs index dbef53c9799..dbc1430f7ea 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstExpression.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstExpression.cs @@ -256,12 +256,12 @@ public static AstExpression Constant(BsonValue value) } public static AstExpression Convert(AstExpression input, AstExpression to, AstExpression onError = null, AstExpression onNull = null, - AstExpression subType = null, AstExpression format = null) + AstExpression subType = null, string format = null, Mql.ByteOrder? byteOrder = null) { Ensure.IsNotNull(input, nameof(input)); Ensure.IsNotNull(to, nameof(to)); - if (onError == null && onNull == null && subType == null && format == null && + if (onError == null && onNull == null && subType == null && format == null && byteOrder == null && to is AstConstantExpression toConstantExpression && (toConstantExpression.Value as BsonString)?.Value is { } toValue) { @@ -284,7 +284,7 @@ to is AstConstantExpression toConstantExpression && } } - return new AstConvertExpression(input, to, onError, onNull, subType, format); + return new AstConvertExpression(input, to, onError, onNull, subType, format, byteOrder); } public static AstExpression DateAdd( diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Visitors/AstNodeVisitor.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Visitors/AstNodeVisitor.cs index fe8cff3cc16..df5db0576f5 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Visitors/AstNodeVisitor.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Visitors/AstNodeVisitor.cs @@ -227,7 +227,7 @@ public virtual AstNode VisitConstantExpression(AstConstantExpression node) public virtual AstNode VisitConvertExpression(AstConvertExpression node) { return node.Update(VisitAndConvert(node.Input), VisitAndConvert(node.To), VisitAndConvert(node.OnError), VisitAndConvert(node.OnNull), - VisitAndConvert(node.SubType), VisitAndConvert(node.Format)); + VisitAndConvert(node.SubType), VisitAndConvert(node.Format), VisitAndConvert(node.ByteOrder)); } public virtual AstNode VisitCountStage(AstCountStage node) diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/MqlMethod.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/MqlMethod.cs index cc96247c798..d56f20ef1df 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/MqlMethod.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/MqlMethod.cs @@ -66,27 +66,27 @@ static MqlMethod() __isNullOrMissing = ReflectionInfo.Method((object field) => Mql.IsNullOrMissing(field)); // Convert methods - __convertToBinDataFromDouble = ReflectionInfo.Method((double? field, BsonBinarySubType subType, string format) => Mql.ConvertToBinData(field, subType, format)); - __convertToBinDataFromDoubleWithOnErrorAndOnNull = ReflectionInfo.Method((double? field, BsonBinarySubType subType, string format, BsonBinaryData onError, BsonBinaryData onNull) - => Mql.ConvertToBinData(field, subType, format, onError, onNull)); - __convertToBinDataFromInt = ReflectionInfo.Method((int? field, BsonBinarySubType subType, string format) => Mql.ConvertToBinData(field, subType, format)); - __convertToBinDataFromIntWithOnErrorAndOnNull = ReflectionInfo.Method((int? field, BsonBinarySubType subType, string format, BsonBinaryData onError, BsonBinaryData onNull) - => Mql.ConvertToBinData(field, subType, format, onError, onNull)); - __convertToBinDataFromLong = ReflectionInfo.Method((long? field, BsonBinarySubType subType, string format) => Mql.ConvertToBinData(field, subType, format)); - __convertToBinDataFromLongWithOnErrorAndOnNull = ReflectionInfo.Method((long? field, BsonBinarySubType subType, string format, BsonBinaryData onError, BsonBinaryData onNull) - => Mql.ConvertToBinData(field, subType, format, onError, onNull)); + __convertToBinDataFromDouble = ReflectionInfo.Method((double? field, BsonBinarySubType subType, Mql.ByteOrder byteOrder) => Mql.ConvertToBinData(field, subType, byteOrder)); + __convertToBinDataFromDoubleWithOnErrorAndOnNull = ReflectionInfo.Method((double? field, BsonBinarySubType subType, Mql.ByteOrder byteOrder, BsonBinaryData onError, BsonBinaryData onNull) + => Mql.ConvertToBinData(field, subType, byteOrder, onError, onNull)); + __convertToBinDataFromInt = ReflectionInfo.Method((int? field, BsonBinarySubType subType, Mql.ByteOrder byteOrder) => Mql.ConvertToBinData(field, subType, byteOrder)); + __convertToBinDataFromIntWithOnErrorAndOnNull = ReflectionInfo.Method((int? field, BsonBinarySubType subType, Mql.ByteOrder byteOrder, BsonBinaryData onError, BsonBinaryData onNull) + => Mql.ConvertToBinData(field, subType, byteOrder, onError, onNull)); + __convertToBinDataFromLong = ReflectionInfo.Method((long? field, BsonBinarySubType subType, Mql.ByteOrder byteOrder) => Mql.ConvertToBinData(field, subType, byteOrder)); + __convertToBinDataFromLongWithOnErrorAndOnNull = ReflectionInfo.Method((long? field, BsonBinarySubType subType, Mql.ByteOrder byteOrder, BsonBinaryData onError, BsonBinaryData onNull) + => Mql.ConvertToBinData(field, subType, byteOrder, onError, onNull)); __convertToBinDataFromString = ReflectionInfo.Method((string field, BsonBinarySubType subType, string format) => Mql.ConvertToBinData(field, subType, format)); __convertToBinDataFromStringWithOnErrorAndOnNull = ReflectionInfo.Method((string field, BsonBinarySubType subType, string format, BsonBinaryData onError, BsonBinaryData onNull) => Mql.ConvertToBinData(field, subType, format, onError, onNull)); - __convertToDoubleFromBinData = ReflectionInfo.Method((BsonBinaryData field, string format) => Mql.ConvertToDouble(field, format)); - __convertToDoubleFromBinDataWithOnErrorAndOnNull = ReflectionInfo.Method((BsonBinaryData field, string format, double? onError, double? onNull) => Mql.ConvertToDouble(field, format, onError, onNull)); + __convertToDoubleFromBinData = ReflectionInfo.Method((BsonBinaryData field, Mql.ByteOrder byteOrder) => Mql.ConvertToDouble(field, byteOrder)); + __convertToDoubleFromBinDataWithOnErrorAndOnNull = ReflectionInfo.Method((BsonBinaryData field, Mql.ByteOrder byteOrder, double? onError, double? onNull) => Mql.ConvertToDouble(field, byteOrder, onError, onNull)); - __convertToIntFromBinData = ReflectionInfo.Method((BsonBinaryData field, string format) => Mql.ConvertToInt(field, format)); - __convertToIntFromBinDataWithOnErrorAndOnNull = ReflectionInfo.Method((BsonBinaryData field, string format, int? onError, int? onNull) => Mql.ConvertToInt(field, format, onError, onNull)); + __convertToIntFromBinData = ReflectionInfo.Method((BsonBinaryData field, Mql.ByteOrder byteOrder) => Mql.ConvertToInt(field, byteOrder)); + __convertToIntFromBinDataWithOnErrorAndOnNull = ReflectionInfo.Method((BsonBinaryData field, Mql.ByteOrder byteOrder, int? onError, int? onNull) => Mql.ConvertToInt(field, byteOrder, onError, onNull)); - __convertToLongFromBinData = ReflectionInfo.Method((BsonBinaryData field, string format) => Mql.ConvertToLong(field, format)); - __convertToLongFromBinDataWithOnErrorAndOnNull = ReflectionInfo.Method((BsonBinaryData field, string format, long? onError, long? onNull) => Mql.ConvertToLong(field, format, onError, onNull)); + __convertToLongFromBinData = ReflectionInfo.Method((BsonBinaryData field, Mql.ByteOrder byteOrder) => Mql.ConvertToLong(field, byteOrder)); + __convertToLongFromBinDataWithOnErrorAndOnNull = ReflectionInfo.Method((BsonBinaryData field, Mql.ByteOrder byteOrder, long? onError, long? onNull) => Mql.ConvertToLong(field, byteOrder, onError, onNull)); __convertToStringFromBinData = ReflectionInfo.Method((BsonBinaryData field, string format) => Mql.ConvertToString(field, format)); __convertToStringFromBinDataWithOnErrorAndOnNull = ReflectionInfo.Method((BsonBinaryData field, string format, string onError, string onNull) => Mql.ConvertToString(field, format, onError, onNull)); diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs index be907e0be78..81c612648c0 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs @@ -13,20 +13,22 @@ namespace MongoDB.Driver.Linq.Linq3Implementation.Translators.ExpressionToAggreg { internal class ConvertMethodToAggregationExpressionTranslator { - private static readonly List<(MethodInfo[] Methods, IBsonSerializer Serializer, BsonType Type, int? FormatIndex, int? SubTypeIndex, int? OnErrorIndex, int? OnNullIndex)> _methodMappings = + private static readonly List<(MethodInfo[] Methods, IBsonSerializer Serializer, BsonType Type, int? FormatIndex, int? SubTypeIndex, int? ByteOrderIndex, int? OnErrorIndex, int? OnNullIndex)> _methodMappings = [ - ([MqlMethod.ConvertToBinDataFromString, MqlMethod.ConvertToBinDataFromInt, MqlMethod.ConvertToBinDataFromLong, MqlMethod.ConvertToBinDataFromDouble], - BsonBinaryDataSerializer.Instance, BsonType.Binary, 2, 1, null, null), - ([MqlMethod.ConvertToBinDataFromStringWithOnErrorAndOnNull, MqlMethod.ConvertToBinDataFromIntWithOnErrorAndOnNull, MqlMethod.ConvertToBinDataFromLongWithOnErrorAndOnNull, MqlMethod.ConvertToBinDataFromDoubleWithOnErrorAndOnNull], - BsonBinaryDataSerializer.Instance, BsonType.Binary, 2, 1, 3, 4), - ([MqlMethod.ConvertToStringFromBinData], StringSerializer.Instance, BsonType.String, 1, null, null, null), - ([MqlMethod.ConvertToStringFromBinDataWithOnErrorAndOnNull], StringSerializer.Instance, BsonType.String, 1, null, 2, 3), - ([MqlMethod.ConvertToIntFromBinData], new NullableSerializer(Int32Serializer.Instance), BsonType.Int32, 1, null, null, null), - ([MqlMethod.ConvertToIntFromBinDataWithOnErrorAndOnNull], new NullableSerializer(Int32Serializer.Instance), BsonType.Int32, 1, null, 2, 3), - ([MqlMethod.ConvertToLongFromBinData], new NullableSerializer(Int64Serializer.Instance), BsonType.Int64, 1, null, null, null), - ([MqlMethod.ConvertToLongFromBinDataWithOnErrorAndOnNull], new NullableSerializer(Int64Serializer.Instance), BsonType.Int64, 1, null, 2, 3), - ([MqlMethod.ConvertToDoubleFromBinData], new NullableSerializer(DoubleSerializer.Instance), BsonType.Double, 1, null, null, null), - ([MqlMethod.ConvertToDoubleFromBinDataWithOnErrorAndOnNull], new NullableSerializer(DoubleSerializer.Instance), BsonType.Double, 1, null, 2, 3) + ([MqlMethod.ConvertToBinDataFromString], BsonBinaryDataSerializer.Instance, BsonType.Binary, 2, 1, null, null, null), + ([MqlMethod.ConvertToBinDataFromInt, MqlMethod.ConvertToBinDataFromLong, MqlMethod.ConvertToBinDataFromDouble], + BsonBinaryDataSerializer.Instance, BsonType.Binary, null, 1, 2, null, null), + ([MqlMethod.ConvertToBinDataFromStringWithOnErrorAndOnNull], BsonBinaryDataSerializer.Instance, BsonType.Binary, 2, 1, null, 3, 4), + ([MqlMethod.ConvertToBinDataFromIntWithOnErrorAndOnNull, MqlMethod.ConvertToBinDataFromLongWithOnErrorAndOnNull, MqlMethod.ConvertToBinDataFromDoubleWithOnErrorAndOnNull], + BsonBinaryDataSerializer.Instance, BsonType.Binary, null, 1, 2, 3, 4), + ([MqlMethod.ConvertToStringFromBinData], StringSerializer.Instance, BsonType.String, 1, null, null, null, null), + ([MqlMethod.ConvertToStringFromBinDataWithOnErrorAndOnNull], StringSerializer.Instance, BsonType.String, 1, null, null, 2, 3), + ([MqlMethod.ConvertToIntFromBinData], new NullableSerializer(Int32Serializer.Instance), BsonType.Int32, 1, null, null, null, null), + ([MqlMethod.ConvertToIntFromBinDataWithOnErrorAndOnNull], new NullableSerializer(Int32Serializer.Instance), BsonType.Int32, 1, null, null, 2, 3), + ([MqlMethod.ConvertToLongFromBinData], new NullableSerializer(Int64Serializer.Instance), BsonType.Int64, 1, null, null, null, null), + ([MqlMethod.ConvertToLongFromBinDataWithOnErrorAndOnNull], new NullableSerializer(Int64Serializer.Instance), BsonType.Int64, 1, null, null, 2, 3), + ([MqlMethod.ConvertToDoubleFromBinData], new NullableSerializer(DoubleSerializer.Instance), BsonType.Double, 1, null, null, null, null), + ([MqlMethod.ConvertToDoubleFromBinDataWithOnErrorAndOnNull], new NullableSerializer(DoubleSerializer.Instance), BsonType.Double, 1, null, null, 2, 3) ]; public static TranslatedExpression Translate(TranslationContext context, MethodCallExpression expression) { @@ -37,14 +39,68 @@ public static TranslatedExpression Translate(TranslationContext context, MethodC if (mapping == default) throw new ExpressionNotSupportedException(expression); + Mql.ByteOrder? byteOrder = null; + string format = null; + var fieldAst = ExpressionToAggregationExpressionTranslator.Translate(context, arguments[0]).Ast; - var formatAst = mapping.FormatIndex.HasValue ? ExpressionToAggregationExpressionTranslator.Translate(context, arguments[mapping.FormatIndex.Value]).Ast : null; var subTypeAst = mapping.SubTypeIndex.HasValue ? ExpressionToAggregationExpressionTranslator.Translate(context, arguments[mapping.SubTypeIndex.Value]).Ast : null; var onErrorAst = mapping.OnErrorIndex.HasValue ? ExpressionToAggregationExpressionTranslator.Translate(context, arguments[mapping.OnErrorIndex.Value]).Ast : null; var onNullAst = mapping.OnNullIndex.HasValue ? ExpressionToAggregationExpressionTranslator.Translate(context, arguments[mapping.OnNullIndex.Value]).Ast : null; - var ast = AstExpression.Convert(fieldAst, AstExpression.Constant(mapping.Type), onError: onErrorAst, onNull: onNullAst, subType: subTypeAst, format: formatAst); + if (mapping.ByteOrderIndex.HasValue) + { + if (arguments[mapping.ByteOrderIndex.Value] is ConstantExpression co) + { + byteOrder = (Mql.ByteOrder)co.Value!; + } + else + { + throw new InvalidOperationException("The 'byteOrder' argument must be a constant expression"); //TODO Improve exception + } + } + + if (mapping.FormatIndex.HasValue) + { + if (arguments[mapping.FormatIndex.Value] is ConstantExpression co) + { + format = (string)co.Value!; + } + else + { + throw new InvalidOperationException("The 'format' argument must be a constant expression"); //TODO Improve exception + } + } + + var ast = AstExpression.Convert(fieldAst, MapBsonTypeToString(mapping.Type), onError: onErrorAst, onNull: onNullAst, subType: subTypeAst, format: format, byteOrder: byteOrder); return new TranslatedExpression(expression, ast, mapping.Serializer); } + + private static string MapBsonTypeToString(BsonType type) //TODO need to find a good place for this + { + return type switch + { + BsonType.Array => "array", + BsonType.Binary => "binData", + BsonType.Boolean => "bool", + BsonType.DateTime => "date", + BsonType.Decimal128 => "decimal", + BsonType.Document => "object", + BsonType.Double => "double", + BsonType.Int32 => "int", + BsonType.Int64 => "long", + BsonType.JavaScript => "javascript", + BsonType.JavaScriptWithScope => "javascriptWithScope", + BsonType.MaxKey => "maxKey", + BsonType.MinKey => "minKey", + BsonType.Null => "null", + BsonType.ObjectId => "objectId", + BsonType.RegularExpression => "regex", + BsonType.String => "string", + BsonType.Symbol => "symbol", + BsonType.Timestamp => "timestamp", + BsonType.Undefined => "undefined", + _ => throw new ArgumentException($"Unexpected BSON type: {type}.", nameof(type)) + }; + } } } diff --git a/src/MongoDB.Driver/Mql.cs b/src/MongoDB.Driver/Mql.cs index de657a01ad5..7c409f022bf 100644 --- a/src/MongoDB.Driver/Mql.cs +++ b/src/MongoDB.Driver/Mql.cs @@ -49,7 +49,6 @@ public static TValue Constant(TValue value, BsonType representaion) throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } - /// /// Converts a string to a BsonBinaryData using the $convert aggregation operator. /// @@ -82,9 +81,9 @@ public static BsonBinaryData ConvertToBinData(string value, BsonBinarySubType su /// /// The value. /// The BsonBinaryData subtype of the result value. - /// The format string. + /// The byte order of BsonBinaryData. /// The converted value. - public static BsonBinaryData ConvertToBinData(int? value, BsonBinarySubType subtype, string format) + public static BsonBinaryData ConvertToBinData(int? value, BsonBinarySubType subtype, ByteOrder byteOrder) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } @@ -94,12 +93,12 @@ public static BsonBinaryData ConvertToBinData(int? value, BsonBinarySubType subt /// /// The value. /// The BsonBinaryData subtype of the result value. - /// The format string. + /// The byte ordering of BsonBinaryData. /// The onError value. /// The onNull value. /// The converted value. /// - public static BsonBinaryData ConvertToBinData(int? value, BsonBinarySubType subtype, string format, + public static BsonBinaryData ConvertToBinData(int? value, BsonBinarySubType subtype, ByteOrder byteOrder, BsonBinaryData onError, BsonBinaryData onNull) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); @@ -110,9 +109,9 @@ public static BsonBinaryData ConvertToBinData(int? value, BsonBinarySubType subt /// /// The value. /// The BsonBinaryData subtype of the result value. - /// The format string. + /// The byte ordering of BsonBinaryData. /// The converted value. - public static BsonBinaryData ConvertToBinData(long? value, BsonBinarySubType subtype, string format) + public static BsonBinaryData ConvertToBinData(long? value, BsonBinarySubType subtype, ByteOrder byteOrder) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } @@ -122,12 +121,12 @@ public static BsonBinaryData ConvertToBinData(long? value, BsonBinarySubType sub /// /// The value. /// The BsonBinaryData subtype of the result value. - /// The format string. + /// The byte ordering of BsonBinaryData. /// The onError value. /// The onNull value. /// The converted value. /// - public static BsonBinaryData ConvertToBinData(long? value, BsonBinarySubType subtype, string format, + public static BsonBinaryData ConvertToBinData(long? value, BsonBinarySubType subtype, ByteOrder byteOrder, BsonBinaryData onError, BsonBinaryData onNull) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); @@ -138,9 +137,9 @@ public static BsonBinaryData ConvertToBinData(long? value, BsonBinarySubType sub /// /// The value. /// The BsonBinaryData subtype of the result value. - /// The format string. + /// The byte ordering of BsonBinaryData. /// The converted value. - public static BsonBinaryData ConvertToBinData(double? value, BsonBinarySubType subtype, string format) + public static BsonBinaryData ConvertToBinData(double? value, BsonBinarySubType subtype, ByteOrder byteOrder) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } @@ -150,12 +149,12 @@ public static BsonBinaryData ConvertToBinData(double? value, BsonBinarySubType s /// /// The field. /// The BsonBinaryData subtype of the result value. - /// The format string. + /// The byte ordering of BsonBinaryData. /// The onError value. /// The onNull value. /// The converted value. /// - public static BsonBinaryData ConvertToBinData(double? value, BsonBinarySubType subtype, string format, + public static BsonBinaryData ConvertToBinData(double? value, BsonBinarySubType subtype, ByteOrder byteOrder, BsonBinaryData onError, BsonBinaryData onNull) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); @@ -189,9 +188,9 @@ public static string ConvertToString(BsonBinaryData value, string format, string /// Converts a BsonBinaryData to int? using the $convert aggregation operator. /// /// The value. - /// The format string. + /// The byte ordering of BsonBinaryData. /// The converted value. - public static int? ConvertToInt(BsonBinaryData value, string format) + public static int? ConvertToInt(BsonBinaryData value, ByteOrder byteOrder) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } @@ -200,11 +199,11 @@ public static string ConvertToString(BsonBinaryData value, string format, string /// Converts a BsonBinaryData to int? using the $convert aggregation operator. /// /// The value. - /// The format string. + /// The byte ordering of BsonBinaryData. /// The onError value. /// The onNull value. /// The converted value. - public static int? ConvertToInt(BsonBinaryData value, string format, int? onError, int? onNull) + public static int? ConvertToInt(BsonBinaryData value, ByteOrder byteOrder, int? onError, int? onNull) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } @@ -213,9 +212,9 @@ public static string ConvertToString(BsonBinaryData value, string format, string /// Converts a BsonBinaryData to long? using the $convert aggregation operator. /// /// The value. - /// The format string. + /// The byte ordering of BsonBinaryData. /// The converted value. - public static long? ConvertToLong(BsonBinaryData value, string format) + public static long? ConvertToLong(BsonBinaryData value, ByteOrder byteOrder) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } @@ -224,11 +223,11 @@ public static string ConvertToString(BsonBinaryData value, string format, string /// Converts a BsonBinaryData to long? using the $convert aggregation operator. /// /// The value. - /// The format string. + /// The byte ordering of BsonBinaryData. /// The onError value. /// The onNull value. /// The converted value. - public static long? ConvertToLong(BsonBinaryData value, string format, long? onError, long? onNull) + public static long? ConvertToLong(BsonBinaryData value, ByteOrder byteOrder, long? onError, long? onNull) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } @@ -237,9 +236,9 @@ public static string ConvertToString(BsonBinaryData value, string format, string /// Converts a BsonBinaryData to double? using the $convert aggregation operator. /// /// The value. - /// The format string. + /// The byte ordering of BsonBinaryData. /// The converted value. - public static double? ConvertToDouble(BsonBinaryData value, string format) + public static double? ConvertToDouble(BsonBinaryData value, ByteOrder byteOrder) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } @@ -248,11 +247,11 @@ public static string ConvertToString(BsonBinaryData value, string format, string /// Converts a BsonBinaryData to string using the $convert aggregation operator. /// /// The value. - /// The format string. + /// The byte ordering of BsonBinaryData. /// The onError value. /// The onNull value. /// The converted value. - public static double? ConvertToDouble(BsonBinaryData value, string format, double? onError, double? onNull) + public static double? ConvertToDouble(BsonBinaryData value, ByteOrder byteOrder, double? onError, double? onNull) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } @@ -360,5 +359,20 @@ public static bool IsNullOrMissing(TField field) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } + + /// + /// + /// + public enum ByteOrder + { + /// + /// + /// + BigEndian, + /// + /// + /// + LittleEndian, + } } } diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs index 3950ac7df8b..413437b9f2d 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs @@ -27,6 +27,7 @@ namespace MongoDB.Driver.Tests.Linq.Linq3Implementation.Translators.ExpressionToAggregationExpressionTranslators.MethodTranslators { + //TODO Need to fix tests public class ConvertMethodToAggregationExpressionTranslatorTests : LinqIntegrationTest { @@ -47,13 +48,13 @@ public void MongoDBFunctions_ConvertToBinDataFromDouble_should_work(int id, stri var collection = Fixture.Collection; var queryable = collection.AsQueryable() .Where(x => x.Id == id) - .Select(x => Mql.ConvertToBinData(x.DoubleProperty, BsonBinarySubType.Binary, "hex")); + .Select(x => Mql.ConvertToBinData(x.DoubleProperty, BsonBinarySubType.Binary, Mql.ByteOrder.BigEndian)); var expectedStages = new[] { $"{{ $match : {{ _id : {id} }} }}", - $"{{ $project: {{ _v : {{ $convert : {{ input : '$DoubleProperty', to : {{ type: 5, subtype: 0 }}, format : 'hex' }} }}, _id : 0 }} }}", + $"{{ $project: {{ _v : {{ $convert : {{ input : '$DoubleProperty', to : {{ type: 'binData', subtype: 0 }}, format : 'hex' }} }}, _id : 0 }} }}", }; var expectedResult = expectedBase64 is null? null : new BsonBinaryData(Convert.FromBase64String(expectedBase64)); @@ -75,7 +76,7 @@ public void MongoDBFunctions_ConvertToBinDataFromDoubleWithOnErrorAndOnNull_shou var collection = Fixture.Collection; var queryable = collection.AsQueryable() .Where(x => x.Id == id) - .Select(x => Mql.ConvertToBinData(x.DoubleProperty, BsonBinarySubType.Function, "base64", onErrorBinData, onNullBinData)); + .Select(x => Mql.ConvertToBinData(x.DoubleProperty, BsonBinarySubType.Function, Mql.ByteOrder.BigEndian, onErrorBinData, onNullBinData)); var onErrorString = onErrorBase64 == null ? "null" : $"BinData(0, '{onErrorBase64}')"; var onNullString = onNullBase64 == null ? "null" : $"BinData(0, '{onNullBase64}')"; @@ -83,7 +84,7 @@ public void MongoDBFunctions_ConvertToBinDataFromDoubleWithOnErrorAndOnNull_shou new[] { $"{{ $match : {{ _id : {id} }} }}", - $"{{ $project: {{ _v : {{ $convert : {{ input : '$DoubleProperty', to : {{ type: 5, subtype: 1 }}, onError: {onErrorString}, onNull: {onNullString}, format : 'base64' }} }}, _id : 0 }} }}", + $"{{ $project: {{ _v : {{ $convert : {{ input : '$DoubleProperty', to : {{ type: 'binData', subtype: 1 }}, onError: {onErrorString}, onNull: {onNullString}, format : 'base64' }} }}, _id : 0 }} }}", }; var expectedResult = expectedBase64 is null? null : new BsonBinaryData(Convert.FromBase64String(expectedBase64)); @@ -102,13 +103,13 @@ public void MongoDBFunctions_ConvertToDoubleFromBinData_should_work(int id, doub var collection = Fixture.Collection; var queryable = collection.AsQueryable() .Where(x => x.Id == id) - .Select(x => Mql.ConvertToDouble(x.BinaryProperty, "hex")); + .Select(x => Mql.ConvertToDouble(x.BinaryProperty, Mql.ByteOrder.BigEndian)); var expectedStages = new[] { $"{{ $match : {{ _id : {id} }} }}", - $"{{ $project: {{ _v : {{ $convert : {{ input : '$BinaryProperty', to : 1, format : 'hex' }} }}, _id : 0 }} }}", + $"{{ $project: {{ _v : {{ $convert : {{ input : '$BinaryProperty', to : 'double', format : 'hex' }} }}, _id : 0 }} }}", }; AssertOutcome(collection, queryable, expectedStages, expectedResult, expectedException); @@ -126,7 +127,7 @@ public void MongoDBFunctions_ConvertToDoubleFromBinDataWithOnErrorAndOnNull_shou var collection = Fixture.Collection; var queryable = collection.AsQueryable() .Where(x => x.Id == id) - .Select(x => Mql.ConvertToDouble(x.BinaryProperty, "hex", onError, onNull)); + .Select(x => Mql.ConvertToDouble(x.BinaryProperty, Mql.ByteOrder.BigEndian, onError, onNull)); var onErrorString = onError == null ? "null" : onError.Value.ToString(NumberFormatInfo.InvariantInfo); var onNullString = onNull == null ? "null" : onNull.Value.ToString(NumberFormatInfo.InvariantInfo); @@ -134,7 +135,7 @@ public void MongoDBFunctions_ConvertToDoubleFromBinDataWithOnErrorAndOnNull_shou new[] { $"{{ $match : {{ _id : {id} }} }}", - $"{{ $project: {{ _v : {{ $convert : {{ input : '$BinaryProperty', to : 1, onError: {onErrorString}, onNull: {onNullString}, format : 'hex' }} }}, _id : 0 }} }}", + $"{{ $project: {{ _v : {{ $convert : {{ input : '$BinaryProperty', to : 'double', onError: {onErrorString}, onNull: {onNullString}, format : 'hex' }} }}, _id : 0 }} }}", }; AssertOutcome(collection, queryable, expectedStages, expectedResult); @@ -152,13 +153,13 @@ public void MongoDBFunctions_ConvertToIntFromBinData_should_work(int id, int? ex var collection = Fixture.Collection; var queryable = collection.AsQueryable() .Where(x => x.Id == id) - .Select(x => Mql.ConvertToInt(x.BinaryProperty, "hex")); + .Select(x => Mql.ConvertToInt(x.BinaryProperty, Mql.ByteOrder.BigEndian)); var expectedStages = new[] { $"{{ $match : {{ _id : {id} }} }}", - $"{{ $project: {{ _v : {{ $convert : {{ input : '$BinaryProperty', to : 16, format : 'hex' }} }}, _id : 0 }} }}", + $"{{ $project: {{ _v : {{ $convert : {{ input : '$BinaryProperty', to : 'int', format : 'hex' }} }}, _id : 0 }} }}", }; AssertOutcome(collection, queryable, expectedStages, expectedResult, expectedException); @@ -176,7 +177,7 @@ public void MongoDBFunctions_ConvertToIntFromBinDataWithOnErrorAndOnNull_should_ var collection = Fixture.Collection; var queryable = collection.AsQueryable() .Where(x => x.Id == id) - .Select(x => Mql.ConvertToInt(x.BinaryProperty, "hex", onError, onNull)); + .Select(x => Mql.ConvertToInt(x.BinaryProperty, Mql.ByteOrder.BigEndian, onError, onNull)); var onErrorString = onError == null ? "null" : onError.Value.ToString(NumberFormatInfo.InvariantInfo); var onNullString = onNull == null ? "null" : onNull.Value.ToString(NumberFormatInfo.InvariantInfo); @@ -184,7 +185,7 @@ public void MongoDBFunctions_ConvertToIntFromBinDataWithOnErrorAndOnNull_should_ new[] { $"{{ $match : {{ _id : {id} }} }}", - $"{{ $project: {{ _v : {{ $convert : {{ input : '$BinaryProperty', to : 16, onError: {onErrorString}, onNull: {onNullString}, format : 'hex' }} }}, _id : 0 }} }}", + $"{{ $project: {{ _v : {{ $convert : {{ input : '$BinaryProperty', to : 'int', onError: {onErrorString}, onNull: {onNullString}, format : 'hex' }} }}, _id : 0 }} }}", }; AssertOutcome(collection, queryable, expectedStages, expectedResult); @@ -202,13 +203,13 @@ public void MongoDBFunctions_ConvertToLongFromBinData_should_work(int id, long? var collection = Fixture.Collection; var queryable = collection.AsQueryable() .Where(x => x.Id == id) - .Select(x => Mql.ConvertToLong(x.BinaryProperty, "hex")); + .Select(x => Mql.ConvertToLong(x.BinaryProperty, Mql.ByteOrder.BigEndian)); var expectedStages = new[] { $"{{ $match : {{ _id : {id} }} }}", - $"{{ $project: {{ _v : {{ $convert : {{ input : '$BinaryProperty', to : 18, format : 'hex' }} }}, _id : 0 }} }}", + $"{{ $project: {{ _v : {{ $convert : {{ input : '$BinaryProperty', to : 'long', format : 'hex' }} }}, _id : 0 }} }}", }; AssertOutcome(collection, queryable, expectedStages, expectedResult, expectedException); @@ -226,7 +227,7 @@ public void MongoDBFunctions_ConvertToLongFromBinDataWithOnErrorAndOnNull_should var collection = Fixture.Collection; var queryable = collection.AsQueryable() .Where(x => x.Id == id) - .Select(x => Mql.ConvertToLong(x.BinaryProperty, "hex", onError, onNull)); + .Select(x => Mql.ConvertToLong(x.BinaryProperty, Mql.ByteOrder.BigEndian, onError, onNull)); var onErrorString = onError == null ? "null" : onError.Value.ToString(NumberFormatInfo.InvariantInfo); var onNullString = onNull == null ? "null" : onNull.Value.ToString(NumberFormatInfo.InvariantInfo); @@ -234,7 +235,7 @@ public void MongoDBFunctions_ConvertToLongFromBinDataWithOnErrorAndOnNull_should new[] { $"{{ $match : {{ _id : {id} }} }}", - $"{{ $project: {{ _v : {{ $convert : {{ input : '$BinaryProperty', to : 18, onError: {onErrorString}, onNull: {onNullString}, format : 'hex' }} }}, _id : 0 }} }}", + $"{{ $project: {{ _v : {{ $convert : {{ input : '$BinaryProperty', to : 'long', onError: {onErrorString}, onNull: {onNullString}, format : 'hex' }} }}, _id : 0 }} }}", }; AssertOutcome(collection, queryable, expectedStages, expectedResult); @@ -258,7 +259,7 @@ public void MongoDBFunctions_ConvertToStringFromBinData_should_work(int id, stri new[] { $"{{ $match : {{ _id : {id} }} }}", - """{"$project": { "_v" : { "$convert" : { "input" : "$BinaryProperty", "to" : 2, "format" : "uuid" } }, "_id" : 0 }}""", + """{"$project": { "_v" : { "$convert" : { "input" : "$BinaryProperty", "to" : "string", "format" : "uuid" } }, "_id" : 0 }}""", }; AssertOutcome(collection, queryable, expectedStages, expectedResult, expectedException); @@ -285,7 +286,7 @@ public void MongoDBFunctions_ConvertToStringFromBinDataWithOnErrorAndOnNull_shou new[] { $"{{ $match : {{ _id : {id} }} }}", - $"{{ $project: {{ _v : {{ $convert : {{ input : '$BinaryProperty', to : 2, onError: {onErrorString}, onNull: {onNullString}, format : 'uuid' }} }}, _id : 0 }} }}", + $"{{ $project: {{ _v : {{ $convert : {{ input : '$BinaryProperty', to : 'string' , onError: {onErrorString}, onNull: {onNullString}, format : 'uuid' }} }}, _id : 0 }} }}", }; AssertOutcome(collection, queryable, expectedStages, expectedResult); @@ -324,7 +325,8 @@ public sealed class ClassFixture : MongoCollectionFixture new TestClass {Id = 1, BinaryProperty = new BsonBinaryData([0, 1, 2])}, new TestClass {Id = 2, BinaryProperty = new BsonBinaryData(Guid.Parse("867dee52-c331-484e-92d1-c56479b8e67e"), GuidRepresentation.Standard), DoubleProperty = 2.45673345}, new TestClass {Id = 3, BinaryProperty = new BsonBinaryData(Convert.FromBase64String("AAAAAAAA4L8=")), DoubleProperty = -0.5}, - new TestClass {Id = 4, BinaryProperty = new BsonBinaryData(Convert.FromBase64String("Ag=="))} + new TestClass {Id = 4, BinaryProperty = new BsonBinaryData(Convert.FromBase64String("Ag=="))}, + new TestClass {Id = 5, BinaryProperty = new BsonBinaryData(Convert.FromBase64String("wAQAAAAAAAA=")), DoubleProperty = -2.5}, ]; } @@ -337,7 +339,6 @@ public class TestClass public int? IntProperty { get; set; } public long? LongProperty { get; set; } public string StringProperty { get; set; } - } } } \ No newline at end of file From 94651f8dad463d38d29b0f89bdcb635e2e3811dc Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Fri, 21 Mar 2025 17:36:00 +0100 Subject: [PATCH 19/82] Small fix --- .../ConvertMethodToAggregationExpressionTranslatorTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs index 413437b9f2d..d0ecd30521a 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs @@ -63,8 +63,8 @@ public void MongoDBFunctions_ConvertToBinDataFromDouble_should_work(int id, stri [Theory] [InlineData(2, "AAAAAAAA4L8=", "AAAAAAAA4L8=", "AAAAAAAABMA=")] - // [InlineData(0, "AAAAAAAABMA=", "AAAAAAAA4L8=", "AAAAAAAABMA=")] //TODO Don't know how to cause an error - // [InlineData(2, null, null, "AAAAAAAABMA=")] //TODO The issue here is that BsonBinaryDataSerializer can't serialize null values, an exception is thrown (because it's a BsonValueSerializerBase) + // [InlineData(0, "AAAAAAAABMA=", "AAAAAAAA4L8=", "AAAAAAAABMA=")] + // [InlineData(2, null, null, "AAAAAAAABMA=")] // [InlineData(0, null, "AAAAAAAA4L8=", null)] public void MongoDBFunctions_ConvertToBinDataFromDoubleWithOnErrorAndOnNull_should_work(int id, string expectedBase64, string onErrorBase64, string onNullBase64) { From 82b3f0234e603bd431bc261ff4b6568446a19329 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Mon, 24 Mar 2025 09:50:57 +0100 Subject: [PATCH 20/82] Imrpoved tests --- .../Ast/Visitors/AstNodeVisitor.cs | 2 +- ...MethodToAggregationExpressionTranslator.cs | 12 +- ...dToAggregationExpressionTranslatorTests.cs | 110 +++++++++++------- 3 files changed, 73 insertions(+), 51 deletions(-) diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Visitors/AstNodeVisitor.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Visitors/AstNodeVisitor.cs index df5db0576f5..bf9acb51cce 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Visitors/AstNodeVisitor.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Visitors/AstNodeVisitor.cs @@ -227,7 +227,7 @@ public virtual AstNode VisitConstantExpression(AstConstantExpression node) public virtual AstNode VisitConvertExpression(AstConvertExpression node) { return node.Update(VisitAndConvert(node.Input), VisitAndConvert(node.To), VisitAndConvert(node.OnError), VisitAndConvert(node.OnNull), - VisitAndConvert(node.SubType), VisitAndConvert(node.Format), VisitAndConvert(node.ByteOrder)); + VisitAndConvert(node.SubType), node.Format, node.ByteOrder); } public virtual AstNode VisitCountStage(AstCountStage node) diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs index 81c612648c0..884ea6437ae 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs @@ -23,12 +23,12 @@ internal class ConvertMethodToAggregationExpressionTranslator BsonBinaryDataSerializer.Instance, BsonType.Binary, null, 1, 2, 3, 4), ([MqlMethod.ConvertToStringFromBinData], StringSerializer.Instance, BsonType.String, 1, null, null, null, null), ([MqlMethod.ConvertToStringFromBinDataWithOnErrorAndOnNull], StringSerializer.Instance, BsonType.String, 1, null, null, 2, 3), - ([MqlMethod.ConvertToIntFromBinData], new NullableSerializer(Int32Serializer.Instance), BsonType.Int32, 1, null, null, null, null), - ([MqlMethod.ConvertToIntFromBinDataWithOnErrorAndOnNull], new NullableSerializer(Int32Serializer.Instance), BsonType.Int32, 1, null, null, 2, 3), - ([MqlMethod.ConvertToLongFromBinData], new NullableSerializer(Int64Serializer.Instance), BsonType.Int64, 1, null, null, null, null), - ([MqlMethod.ConvertToLongFromBinDataWithOnErrorAndOnNull], new NullableSerializer(Int64Serializer.Instance), BsonType.Int64, 1, null, null, 2, 3), - ([MqlMethod.ConvertToDoubleFromBinData], new NullableSerializer(DoubleSerializer.Instance), BsonType.Double, 1, null, null, null, null), - ([MqlMethod.ConvertToDoubleFromBinDataWithOnErrorAndOnNull], new NullableSerializer(DoubleSerializer.Instance), BsonType.Double, 1, null, null, 2, 3) + ([MqlMethod.ConvertToIntFromBinData], new NullableSerializer(Int32Serializer.Instance), BsonType.Int32, null, null, 1, null, null), + ([MqlMethod.ConvertToIntFromBinDataWithOnErrorAndOnNull], new NullableSerializer(Int32Serializer.Instance), BsonType.Int32, null, null, 1, 2, 3), + ([MqlMethod.ConvertToLongFromBinData], new NullableSerializer(Int64Serializer.Instance), BsonType.Int64, null, null, 1, null, null), + ([MqlMethod.ConvertToLongFromBinDataWithOnErrorAndOnNull], new NullableSerializer(Int64Serializer.Instance), BsonType.Int64, null, null, 1, 2, 3), + ([MqlMethod.ConvertToDoubleFromBinData], new NullableSerializer(DoubleSerializer.Instance), BsonType.Double, null, null, 1, null, null), + ([MqlMethod.ConvertToDoubleFromBinDataWithOnErrorAndOnNull], new NullableSerializer(DoubleSerializer.Instance), BsonType.Double, null, null, 1, 2, 3) ]; public static TranslatedExpression Translate(TranslationContext context, MethodCallExpression expression) { diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs index d0ecd30521a..da7db8d9826 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs @@ -39,22 +39,23 @@ public ConvertMethodToAggregationExpressionTranslatorTests(ClassFixture fixture) // To BinData [Theory] - [InlineData(3, "AAAAAAAA4L8=", null)] - [InlineData(1, null, "FormatException")] - public void MongoDBFunctions_ConvertToBinDataFromDouble_should_work(int id, string expectedBase64, string expectedException) + [InlineData(1, Mql.ByteOrder.BigEndian, null, "FormatException")] + [InlineData(3, Mql.ByteOrder.LittleEndian,"AAAAAAAA4L8=", null)] + [InlineData(5, Mql.ByteOrder.BigEndian, "wAQAAAAAAAA=", null )] + public void MongoDBFunctions_ConvertToBinDataFromDouble_should_work(int id, Mql.ByteOrder byteOrder, string expectedBase64, string expectedException) { RequireServer.Check().Supports(Feature.ConvertBinDataToFromNumeric); var collection = Fixture.Collection; var queryable = collection.AsQueryable() .Where(x => x.Id == id) - .Select(x => Mql.ConvertToBinData(x.DoubleProperty, BsonBinarySubType.Binary, Mql.ByteOrder.BigEndian)); + .Select(x => Mql.ConvertToBinData(x.DoubleProperty, BsonBinarySubType.Binary, byteOrder)); var expectedStages = new[] { $"{{ $match : {{ _id : {id} }} }}", - $"{{ $project: {{ _v : {{ $convert : {{ input : '$DoubleProperty', to : {{ type: 'binData', subtype: 0 }}, format : 'hex' }} }}, _id : 0 }} }}", + $"{{ $project: {{ _v : {{ $convert : {{ input : '$DoubleProperty', to : {{ type: 'binData', subtype: 0 }}, {ByteOrderToString(byteOrder)} }} }}, _id : 0 }} }}", }; var expectedResult = expectedBase64 is null? null : new BsonBinaryData(Convert.FromBase64String(expectedBase64)); @@ -94,40 +95,43 @@ public void MongoDBFunctions_ConvertToBinDataFromDoubleWithOnErrorAndOnNull_shou // To Double [Theory] - [InlineData(3, -0.5, null)] - [InlineData(2, null, "MongoCommandException")] - public void MongoDBFunctions_ConvertToDoubleFromBinData_should_work(int id, double? expectedResult, string expectedException) + [InlineData(2, Mql.ByteOrder.BigEndian, null, "MongoCommandException")] + [InlineData(3, Mql.ByteOrder.LittleEndian, -0.5, null)] + [InlineData(5, Mql.ByteOrder.BigEndian, -2.5, null)] + public void MongoDBFunctions_ConvertToDoubleFromBinData_should_work(int id, Mql.ByteOrder byteOrder, double? expectedResult, string expectedException) { RequireServer.Check().Supports(Feature.ConvertBinDataToFromNumeric); var collection = Fixture.Collection; var queryable = collection.AsQueryable() .Where(x => x.Id == id) - .Select(x => Mql.ConvertToDouble(x.BinaryProperty, Mql.ByteOrder.BigEndian)); + .Select(x => Mql.ConvertToDouble(x.BinaryProperty, byteOrder)); var expectedStages = new[] { $"{{ $match : {{ _id : {id} }} }}", - $"{{ $project: {{ _v : {{ $convert : {{ input : '$BinaryProperty', to : 'double', format : 'hex' }} }}, _id : 0 }} }}", + $"{{ $project: {{ _v : {{ $convert : {{ input : '$BinaryProperty', to : 'double', {ByteOrderToString(byteOrder)} }} }}, _id : 0 }} }}", }; AssertOutcome(collection, queryable, expectedStages, expectedResult, expectedException); } [Theory] - [InlineData(2, 15.2, 15.2, 22.3)] - [InlineData(0, 22.3, 15.2, 22.3)] - [InlineData(2, null, null, 22.3)] - [InlineData(0, null, 15.2, null)] - public void MongoDBFunctions_ConvertToDoubleFromBinDataWithOnErrorAndOnNull_should_work(int id, double? expectedResult, double? onError, double? onNull) + [InlineData(2, Mql.ByteOrder.LittleEndian, 15.2, 15.2, 22.3)] + [InlineData(0, Mql.ByteOrder.LittleEndian, 22.3, 15.2, 22.3)] + [InlineData(2, Mql.ByteOrder.BigEndian, 15.2, 15.2, 22.3)] + [InlineData(0, Mql.ByteOrder.BigEndian, 22.3, 15.2, 22.3)] + [InlineData(2, Mql.ByteOrder.LittleEndian, null, null, 22.3)] + [InlineData(0, Mql.ByteOrder.LittleEndian, null, 15.2, null)] + public void MongoDBFunctions_ConvertToDoubleFromBinDataWithOnErrorAndOnNull_should_work(int id, Mql.ByteOrder byteOrder, double? expectedResult, double? onError, double? onNull) { RequireServer.Check().Supports(Feature.ConvertBinDataToFromNumeric); var collection = Fixture.Collection; var queryable = collection.AsQueryable() .Where(x => x.Id == id) - .Select(x => Mql.ConvertToDouble(x.BinaryProperty, Mql.ByteOrder.BigEndian, onError, onNull)); + .Select(x => Mql.ConvertToDouble(x.BinaryProperty, byteOrder, onError, onNull)); var onErrorString = onError == null ? "null" : onError.Value.ToString(NumberFormatInfo.InvariantInfo); var onNullString = onNull == null ? "null" : onNull.Value.ToString(NumberFormatInfo.InvariantInfo); @@ -135,7 +139,7 @@ public void MongoDBFunctions_ConvertToDoubleFromBinDataWithOnErrorAndOnNull_shou new[] { $"{{ $match : {{ _id : {id} }} }}", - $"{{ $project: {{ _v : {{ $convert : {{ input : '$BinaryProperty', to : 'double', onError: {onErrorString}, onNull: {onNullString}, format : 'hex' }} }}, _id : 0 }} }}", + $"{{ $project: {{ _v : {{ $convert : {{ input : '$BinaryProperty', to : 'double', onError: {onErrorString}, onNull: {onNullString}, {ByteOrderToString(byteOrder)}}} }}, _id : 0 }} }}", }; AssertOutcome(collection, queryable, expectedStages, expectedResult); @@ -144,40 +148,43 @@ public void MongoDBFunctions_ConvertToDoubleFromBinDataWithOnErrorAndOnNull_shou // To Int [Theory] - [InlineData(4, 2, null)] - [InlineData(2, null, "MongoCommandException")] - public void MongoDBFunctions_ConvertToIntFromBinData_should_work(int id, int? expectedResult, string expectedException) + [InlineData(2, Mql.ByteOrder.LittleEndian, null, "MongoCommandException")] + [InlineData(4, Mql.ByteOrder.LittleEndian, 2, null)] + [InlineData(6, Mql.ByteOrder.BigEndian, 42, null)] + public void MongoDBFunctions_ConvertToIntFromBinData_should_work(int id, Mql.ByteOrder byteOrder, int? expectedResult, string expectedException) { RequireServer.Check().Supports(Feature.ConvertBinDataToFromNumeric); var collection = Fixture.Collection; var queryable = collection.AsQueryable() .Where(x => x.Id == id) - .Select(x => Mql.ConvertToInt(x.BinaryProperty, Mql.ByteOrder.BigEndian)); + .Select(x => Mql.ConvertToInt(x.BinaryProperty, byteOrder)); var expectedStages = new[] { $"{{ $match : {{ _id : {id} }} }}", - $"{{ $project: {{ _v : {{ $convert : {{ input : '$BinaryProperty', to : 'int', format : 'hex' }} }}, _id : 0 }} }}", + $"{{ $project: {{ _v : {{ $convert : {{ input : '$BinaryProperty', to : 'int', {ByteOrderToString(byteOrder)} }} }}, _id : 0 }} }}", }; AssertOutcome(collection, queryable, expectedStages, expectedResult, expectedException); } [Theory] - [InlineData(2, 15, 15, 22)] - [InlineData(0, 22, 15, 22)] - [InlineData(2, null, null, 22)] - [InlineData(0, null, 15, null)] - public void MongoDBFunctions_ConvertToIntFromBinDataWithOnErrorAndOnNull_should_work(int id, int? expectedResult, int? onError, int? onNull) + [InlineData(2, Mql.ByteOrder.LittleEndian, 15, 15, 22)] + [InlineData(0, Mql.ByteOrder.LittleEndian, 22, 15, 22)] + [InlineData(2, Mql.ByteOrder.BigEndian, 15, 15, 22)] + [InlineData(0, Mql.ByteOrder.BigEndian, 22, 15, 22)] + [InlineData(2, Mql.ByteOrder.LittleEndian, null, null, 22)] + [InlineData(0, Mql.ByteOrder.LittleEndian, null, 15, null)] + public void MongoDBFunctions_ConvertToIntFromBinDataWithOnErrorAndOnNull_should_work(int id, Mql.ByteOrder byteOrder, int? expectedResult, int? onError, int? onNull) { RequireServer.Check().Supports(Feature.ConvertBinDataToFromNumeric); var collection = Fixture.Collection; var queryable = collection.AsQueryable() .Where(x => x.Id == id) - .Select(x => Mql.ConvertToInt(x.BinaryProperty, Mql.ByteOrder.BigEndian, onError, onNull)); + .Select(x => Mql.ConvertToInt(x.BinaryProperty, byteOrder, onError, onNull)); var onErrorString = onError == null ? "null" : onError.Value.ToString(NumberFormatInfo.InvariantInfo); var onNullString = onNull == null ? "null" : onNull.Value.ToString(NumberFormatInfo.InvariantInfo); @@ -185,7 +192,7 @@ public void MongoDBFunctions_ConvertToIntFromBinDataWithOnErrorAndOnNull_should_ new[] { $"{{ $match : {{ _id : {id} }} }}", - $"{{ $project: {{ _v : {{ $convert : {{ input : '$BinaryProperty', to : 'int', onError: {onErrorString}, onNull: {onNullString}, format : 'hex' }} }}, _id : 0 }} }}", + $"{{ $project: {{ _v : {{ $convert : {{ input : '$BinaryProperty', to : 'int', onError: {onErrorString}, onNull: {onNullString}, {ByteOrderToString(byteOrder)} }} }}, _id : 0 }} }}", }; AssertOutcome(collection, queryable, expectedStages, expectedResult); @@ -194,40 +201,43 @@ public void MongoDBFunctions_ConvertToIntFromBinDataWithOnErrorAndOnNull_should_ // To Long [Theory] - [InlineData(4, (long)2, null)] - [InlineData(2, null, "MongoCommandException")] - public void MongoDBFunctions_ConvertToLongFromBinData_should_work(int id, long? expectedResult, string expectedException) + [InlineData(2, Mql.ByteOrder.LittleEndian, null, "MongoCommandException")] + [InlineData(4, Mql.ByteOrder.LittleEndian, (long)2, null)] + [InlineData(6, Mql.ByteOrder.BigEndian, (long)42, null)] + public void MongoDBFunctions_ConvertToLongFromBinData_should_work(int id, Mql.ByteOrder byteOrder, long? expectedResult, string expectedException) { RequireServer.Check().Supports(Feature.ConvertBinDataToFromNumeric); var collection = Fixture.Collection; var queryable = collection.AsQueryable() .Where(x => x.Id == id) - .Select(x => Mql.ConvertToLong(x.BinaryProperty, Mql.ByteOrder.BigEndian)); + .Select(x => Mql.ConvertToLong(x.BinaryProperty, byteOrder)); var expectedStages = new[] { $"{{ $match : {{ _id : {id} }} }}", - $"{{ $project: {{ _v : {{ $convert : {{ input : '$BinaryProperty', to : 'long', format : 'hex' }} }}, _id : 0 }} }}", + $"{{ $project: {{ _v : {{ $convert : {{ input : '$BinaryProperty', to : 'long', {ByteOrderToString(byteOrder)} }} }}, _id : 0 }} }}", }; AssertOutcome(collection, queryable, expectedStages, expectedResult, expectedException); } [Theory] - [InlineData(2, (long)15, (long)15, (long)22)] - [InlineData(0, (long)22, (long)15, (long)22)] - [InlineData(2, null, null, (long)22)] - [InlineData(0, null, (long)15, null)] - public void MongoDBFunctions_ConvertToLongFromBinDataWithOnErrorAndOnNull_should_work(int id, long? expectedResult, long? onError, long? onNull) + [InlineData(2, Mql.ByteOrder.LittleEndian, (long)15, (long)15, (long)22)] + [InlineData(0, Mql.ByteOrder.LittleEndian, (long)22, (long)15, (long)22)] + [InlineData(2, Mql.ByteOrder.BigEndian, (long)15, (long)15, (long)22)] + [InlineData(0, Mql.ByteOrder.BigEndian, (long)22, (long)15, (long)22)] + [InlineData(2, Mql.ByteOrder.LittleEndian, null, null, (long)22)] + [InlineData(0, Mql.ByteOrder.LittleEndian, null, (long)15, null)] + public void MongoDBFunctions_ConvertToLongFromBinDataWithOnErrorAndOnNull_should_work(int id, Mql.ByteOrder byteOrder, long? expectedResult, long? onError, long? onNull) { RequireServer.Check().Supports(Feature.ConvertBinDataToFromNumeric); var collection = Fixture.Collection; var queryable = collection.AsQueryable() .Where(x => x.Id == id) - .Select(x => Mql.ConvertToLong(x.BinaryProperty, Mql.ByteOrder.BigEndian, onError, onNull)); + .Select(x => Mql.ConvertToLong(x.BinaryProperty, byteOrder, onError, onNull)); var onErrorString = onError == null ? "null" : onError.Value.ToString(NumberFormatInfo.InvariantInfo); var onNullString = onNull == null ? "null" : onNull.Value.ToString(NumberFormatInfo.InvariantInfo); @@ -235,7 +245,7 @@ public void MongoDBFunctions_ConvertToLongFromBinDataWithOnErrorAndOnNull_should new[] { $"{{ $match : {{ _id : {id} }} }}", - $"{{ $project: {{ _v : {{ $convert : {{ input : '$BinaryProperty', to : 'long', onError: {onErrorString}, onNull: {onNullString}, format : 'hex' }} }}, _id : 0 }} }}", + $"{{ $project: {{ _v : {{ $convert : {{ input : '$BinaryProperty', to : 'long', onError: {onErrorString}, onNull: {onNullString}, {ByteOrderToString(byteOrder)} }} }}, _id : 0 }} }}", }; AssertOutcome(collection, queryable, expectedStages, expectedResult); @@ -316,6 +326,17 @@ private void AssertOutcome(IMongoCollection collection, } } + private string ByteOrderToString(Mql.ByteOrder byteOrder) + { + var byteOrderString = byteOrder switch + { + Mql.ByteOrder.BigEndian => "big", + Mql.ByteOrder.LittleEndian => "little", + _ => throw new ArgumentOutOfRangeException(nameof(byteOrder), byteOrder, null) + }; + + return $"byteOrder: '{byteOrderString}'"; + } public sealed class ClassFixture : MongoCollectionFixture { @@ -324,9 +345,10 @@ public sealed class ClassFixture : MongoCollectionFixture new TestClass {Id = 0 }, new TestClass {Id = 1, BinaryProperty = new BsonBinaryData([0, 1, 2])}, new TestClass {Id = 2, BinaryProperty = new BsonBinaryData(Guid.Parse("867dee52-c331-484e-92d1-c56479b8e67e"), GuidRepresentation.Standard), DoubleProperty = 2.45673345}, - new TestClass {Id = 3, BinaryProperty = new BsonBinaryData(Convert.FromBase64String("AAAAAAAA4L8=")), DoubleProperty = -0.5}, - new TestClass {Id = 4, BinaryProperty = new BsonBinaryData(Convert.FromBase64String("Ag=="))}, - new TestClass {Id = 5, BinaryProperty = new BsonBinaryData(Convert.FromBase64String("wAQAAAAAAAA=")), DoubleProperty = -2.5}, + new TestClass {Id = 3, BinaryProperty = new BsonBinaryData(Convert.FromBase64String("AAAAAAAA4L8=")), DoubleProperty = -0.5}, //LittleEndian + new TestClass {Id = 4, BinaryProperty = new BsonBinaryData(Convert.FromBase64String("Ag=="))}, //LittleEndian + new TestClass {Id = 5, BinaryProperty = new BsonBinaryData(Convert.FromBase64String("wAQAAAAAAAA=")), DoubleProperty = -2.5}, //BigEndian + new TestClass {Id = 6, BinaryProperty = new BsonBinaryData(Convert.FromBase64String("AAAAKg=="))}, //BigEndian ]; } From 6bfc02922d17f6ad904db3530ef901a8745d801d Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Mon, 24 Mar 2025 11:15:02 +0100 Subject: [PATCH 21/82] Added tests --- ...dToAggregationExpressionTranslatorTests.cs | 165 ++++++++++++++++-- 1 file changed, 151 insertions(+), 14 deletions(-) diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs index da7db8d9826..be8be73b41d 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs @@ -26,8 +26,6 @@ namespace MongoDB.Driver.Tests.Linq.Linq3Implementation.Translators.ExpressionToAggregationExpressionTranslators.MethodTranslators { - - //TODO Need to fix tests public class ConvertMethodToAggregationExpressionTranslatorTests : LinqIntegrationTest { @@ -63,11 +61,127 @@ public void MongoDBFunctions_ConvertToBinDataFromDouble_should_work(int id, Mql. } [Theory] - [InlineData(2, "AAAAAAAA4L8=", "AAAAAAAA4L8=", "AAAAAAAABMA=")] - // [InlineData(0, "AAAAAAAABMA=", "AAAAAAAA4L8=", "AAAAAAAABMA=")] - // [InlineData(2, null, null, "AAAAAAAABMA=")] - // [InlineData(0, null, "AAAAAAAA4L8=", null)] - public void MongoDBFunctions_ConvertToBinDataFromDoubleWithOnErrorAndOnNull_should_work(int id, string expectedBase64, string onErrorBase64, string onNullBase64) + [InlineData(0, Mql.ByteOrder.LittleEndian, "AAAAAAAABMA=", "Ag==", "AAAAAAAABMA=")] + [InlineData(10, Mql.ByteOrder.LittleEndian, "Ag==", "Ag==", "AAAAAAAABMA=")] + [InlineData(0, Mql.ByteOrder.BigEndian, "AAAAAAAABMA=", "Ag==", "AAAAAAAABMA=")] + [InlineData(10, Mql.ByteOrder.BigEndian, "Ag==", "Ag==", "AAAAAAAABMA=")] + public void MongoDBFunctions_ConvertToBinDataFromDoubleWithOnErrorAndOnNull_should_work(int id, Mql.ByteOrder byteOrder, string expectedBase64, string onErrorBase64, string onNullBase64) + { + RequireServer.Check().Supports(Feature.ConvertBinDataToFromNumeric); + + var onErrorBinData = onErrorBase64 == null ? null : new BsonBinaryData(Convert.FromBase64String(onErrorBase64)); + var onNullBinData = onNullBase64 == null ? null : new BsonBinaryData(Convert.FromBase64String(onNullBase64)); + + var collection = Fixture.Collection; + var queryable = collection.AsQueryable() + .Where(x => x.Id == id) + .Select(x => Mql.ConvertToBinData(x.DoubleProperty, BsonBinarySubType.Binary, byteOrder, onErrorBinData, onNullBinData)); + + var onErrorString = onErrorBase64 == null ? "null" : $"BinData(0, '{onErrorBase64}')"; + var onNullString = onNullBase64 == null ? "null" : $"BinData(0, '{onNullBase64}')"; + var expectedStages = + new[] + { + $"{{ $match : {{ _id : {id} }} }}", + $"{{ $project: {{ _v : {{ $convert : {{ input : '$DoubleProperty', to : {{ type: 'binData', subtype: 0 }}, onError: {onErrorString}, onNull: {onNullString}, {ByteOrderToString(byteOrder)}}} }}, _id : 0 }} }}", + }; + + var expectedResult = expectedBase64 is null? null : new BsonBinaryData(Convert.FromBase64String(expectedBase64)); + AssertOutcome(collection, queryable, expectedStages, expectedResult); + } + + [Theory] + [InlineData(1, Mql.ByteOrder.BigEndian, null, "FormatException")] + [InlineData(4, Mql.ByteOrder.LittleEndian,"ogIAAA==", null)] + [InlineData(6, Mql.ByteOrder.BigEndian, "AAAAKg==", null )] + public void MongoDBFunctions_ConvertToBinDataFromInt_should_work(int id, Mql.ByteOrder byteOrder, string expectedBase64, string expectedException) + { + RequireServer.Check().Supports(Feature.ConvertBinDataToFromNumeric); + + var collection = Fixture.Collection; + var queryable = collection.AsQueryable() + .Where(x => x.Id == id) + .Select(x => Mql.ConvertToBinData(x.IntProperty, BsonBinarySubType.Binary, byteOrder)); + + var expectedStages = + new[] + { + $"{{ $match : {{ _id : {id} }} }}", + $"{{ $project: {{ _v : {{ $convert : {{ input : '$IntProperty', to : {{ type: 'binData', subtype: 0 }}, {ByteOrderToString(byteOrder)} }} }}, _id : 0 }} }}", + }; + + var expectedResult = expectedBase64 is null? null : new BsonBinaryData(Convert.FromBase64String(expectedBase64)); + AssertOutcome(collection, queryable, expectedStages, expectedResult, expectedException); + } + + [Theory] + [InlineData(0, Mql.ByteOrder.LittleEndian, "AAAAAAAABMA=", "Ag==", "AAAAAAAABMA=")] + [InlineData(10, Mql.ByteOrder.LittleEndian, "Ag==", "Ag==", "AAAAAAAABMA=")] + [InlineData(0, Mql.ByteOrder.BigEndian, "AAAAAAAABMA=", "Ag==", "AAAAAAAABMA=")] + [InlineData(10, Mql.ByteOrder.BigEndian, "Ag==", "Ag==", "AAAAAAAABMA=")] + public void MongoDBFunctions_ConvertToBinDataFromIntWithOnErrorAndOnNull_should_work(int id, Mql.ByteOrder byteOrder, string expectedBase64, string onErrorBase64, string onNullBase64) + { + RequireServer.Check().Supports(Feature.ConvertBinDataToFromNumeric); + + var onErrorBinData = onErrorBase64 == null ? null : new BsonBinaryData(Convert.FromBase64String(onErrorBase64)); + var onNullBinData = onNullBase64 == null ? null : new BsonBinaryData(Convert.FromBase64String(onNullBase64)); + + var collection = Fixture.Collection; + var queryable = collection.AsQueryable() + .Where(x => x.Id == id) + .Select(x => Mql.ConvertToBinData(x.IntProperty, BsonBinarySubType.Binary, byteOrder, onErrorBinData, onNullBinData)); + + var onErrorString = onErrorBase64 == null ? "null" : $"BinData(0, '{onErrorBase64}')"; + var onNullString = onNullBase64 == null ? "null" : $"BinData(0, '{onNullBase64}')"; + var expectedStages = + new[] + { + $"{{ $match : {{ _id : {id} }} }}", + $"{{ $project: {{ _v : {{ $convert : {{ input : '$IntProperty', to : {{ type: 'binData', subtype: 0 }}, onError: {onErrorString}, onNull: {onNullString}, {ByteOrderToString(byteOrder)}}} }}, _id : 0 }} }}", + }; + + var expectedResult = expectedBase64 is null? null : new BsonBinaryData(Convert.FromBase64String(expectedBase64)); + AssertOutcome(collection, queryable, expectedStages, expectedResult); + } + + [Theory] + [InlineData(1, Mql.ByteOrder.BigEndian, null, "FormatException")] + [InlineData(4, Mql.ByteOrder.LittleEndian,"ogIAAA==", null)] + [InlineData(6, Mql.ByteOrder.BigEndian, "AAAAKg==", null )] + public void MongoDBFunctions_ConvertToLongDataFromInt_should_work(int id, Mql.ByteOrder byteOrder, string expectedBase64, string expectedException) + { + RequireServer.Check().Supports(Feature.ConvertBinDataToFromNumeric); + + var collection = Fixture.Collection; + var queryable = collection.AsQueryable() + .Where(x => x.Id == id) + .Select(x => Mql.ConvertToBinData(x.LongProperty, BsonBinarySubType.Binary, byteOrder)); + + var expectedStages = + new[] + { + $"{{ $match : {{ _id : {id} }} }}", + $"{{ $project: {{ _v : {{ $convert : {{ input : '$LongProperty', to : {{ type: 'binData', subtype: 0 }}, {ByteOrderToString(byteOrder)} }} }}, _id : 0 }} }}", + }; + + BsonBinaryData expectedResult = null; + if (expectedBase64 is not null) + { + //$convert to bindata returns always 8 bytes when from long + var expectedBytes = new byte[8]; + Array.Copy(Convert.FromBase64String(expectedBase64), 0, expectedBytes, byteOrder is Mql.ByteOrder.LittleEndian ? 0 : 4, 4); + expectedResult = new BsonBinaryData(expectedBytes); + } + + AssertOutcome(collection, queryable, expectedStages, expectedResult, expectedException); + } + + [Theory] + [InlineData(0, Mql.ByteOrder.LittleEndian, "AAAAAAAABMA=", "Ag==", "AAAAAAAABMA=")] + [InlineData(10, Mql.ByteOrder.LittleEndian, "Ag==", "Ag==", "AAAAAAAABMA=")] + [InlineData(0, Mql.ByteOrder.BigEndian, "AAAAAAAABMA=", "Ag==", "AAAAAAAABMA=")] + [InlineData(10, Mql.ByteOrder.BigEndian, "Ag==", "Ag==", "AAAAAAAABMA=")] + public void MongoDBFunctions_ConvertToLongDataFromIntWithOnErrorAndOnNull_should_work(int id, Mql.ByteOrder byteOrder, string expectedBase64, string onErrorBase64, string onNullBase64) { RequireServer.Check().Supports(Feature.ConvertBinDataToFromNumeric); @@ -77,7 +191,7 @@ public void MongoDBFunctions_ConvertToBinDataFromDoubleWithOnErrorAndOnNull_shou var collection = Fixture.Collection; var queryable = collection.AsQueryable() .Where(x => x.Id == id) - .Select(x => Mql.ConvertToBinData(x.DoubleProperty, BsonBinarySubType.Function, Mql.ByteOrder.BigEndian, onErrorBinData, onNullBinData)); + .Select(x => Mql.ConvertToBinData(x.LongProperty, BsonBinarySubType.Binary, byteOrder, onErrorBinData, onNullBinData)); var onErrorString = onErrorBase64 == null ? "null" : $"BinData(0, '{onErrorBase64}')"; var onNullString = onNullBase64 == null ? "null" : $"BinData(0, '{onNullBase64}')"; @@ -85,13 +199,14 @@ public void MongoDBFunctions_ConvertToBinDataFromDoubleWithOnErrorAndOnNull_shou new[] { $"{{ $match : {{ _id : {id} }} }}", - $"{{ $project: {{ _v : {{ $convert : {{ input : '$DoubleProperty', to : {{ type: 'binData', subtype: 1 }}, onError: {onErrorString}, onNull: {onNullString}, format : 'base64' }} }}, _id : 0 }} }}", + $"{{ $project: {{ _v : {{ $convert : {{ input : '$LongProperty', to : {{ type: 'binData', subtype: 0 }}, onError: {onErrorString}, onNull: {onNullString}, {ByteOrderToString(byteOrder)}}} }}, _id : 0 }} }}", }; var expectedResult = expectedBase64 is null? null : new BsonBinaryData(Convert.FromBase64String(expectedBase64)); AssertOutcome(collection, queryable, expectedStages, expectedResult); } + // To Double [Theory] @@ -149,7 +264,7 @@ public void MongoDBFunctions_ConvertToDoubleFromBinDataWithOnErrorAndOnNull_shou [Theory] [InlineData(2, Mql.ByteOrder.LittleEndian, null, "MongoCommandException")] - [InlineData(4, Mql.ByteOrder.LittleEndian, 2, null)] + [InlineData(4, Mql.ByteOrder.LittleEndian, 674, null)] [InlineData(6, Mql.ByteOrder.BigEndian, 42, null)] public void MongoDBFunctions_ConvertToIntFromBinData_should_work(int id, Mql.ByteOrder byteOrder, int? expectedResult, string expectedException) { @@ -202,7 +317,7 @@ public void MongoDBFunctions_ConvertToIntFromBinDataWithOnErrorAndOnNull_should_ [Theory] [InlineData(2, Mql.ByteOrder.LittleEndian, null, "MongoCommandException")] - [InlineData(4, Mql.ByteOrder.LittleEndian, (long)2, null)] + [InlineData(4, Mql.ByteOrder.LittleEndian, (long)674, null)] [InlineData(6, Mql.ByteOrder.BigEndian, (long)42, null)] public void MongoDBFunctions_ConvertToLongFromBinData_should_work(int id, Mql.ByteOrder byteOrder, long? expectedResult, string expectedException) { @@ -340,16 +455,38 @@ private string ByteOrderToString(Mql.ByteOrder byteOrder) public sealed class ClassFixture : MongoCollectionFixture { + + private bool _initialed = false; protected override IEnumerable InitialData { get; } = [ new TestClass {Id = 0 }, new TestClass {Id = 1, BinaryProperty = new BsonBinaryData([0, 1, 2])}, - new TestClass {Id = 2, BinaryProperty = new BsonBinaryData(Guid.Parse("867dee52-c331-484e-92d1-c56479b8e67e"), GuidRepresentation.Standard), DoubleProperty = 2.45673345}, + new TestClass {Id = 2, BinaryProperty = new BsonBinaryData(Guid.Parse("867dee52-c331-484e-92d1-c56479b8e67e"), GuidRepresentation.Standard)}, new TestClass {Id = 3, BinaryProperty = new BsonBinaryData(Convert.FromBase64String("AAAAAAAA4L8=")), DoubleProperty = -0.5}, //LittleEndian - new TestClass {Id = 4, BinaryProperty = new BsonBinaryData(Convert.FromBase64String("Ag=="))}, //LittleEndian + new TestClass {Id = 4, BinaryProperty = new BsonBinaryData(Convert.FromBase64String("ogIAAA==")), IntProperty = 674, LongProperty = 674}, //LittleEndian new TestClass {Id = 5, BinaryProperty = new BsonBinaryData(Convert.FromBase64String("wAQAAAAAAAA=")), DoubleProperty = -2.5}, //BigEndian - new TestClass {Id = 6, BinaryProperty = new BsonBinaryData(Convert.FromBase64String("AAAAKg=="))}, //BigEndian + new TestClass {Id = 6, BinaryProperty = new BsonBinaryData(Convert.FromBase64String("AAAAKg==")), IntProperty = 42, LongProperty = 42}, //BigEndian + new TestClass {Id = 7, BinaryProperty = new BsonBinaryData(Convert.FromBase64String("AQCAfwA="))}, //5 byte single precision double should error //TODO Maybe we don't use this ]; + + protected override void InitializeTestCase() + { + base.InitializeTestCase(); + + if (_initialed) + { + return; + } + + _initialed = true; + + var errorTestCase = "{ _id: 10, DoubleProperty: NumberDecimal('-32768'), IntProperty: NumberDecimal('-32768'), LongProperty: NumberDecimal('-32768') }"; + var parsed = BsonDocument.Parse(errorTestCase); + + var untypedCollection = Database.GetCollection(GetCollectionName()); + + untypedCollection.InsertOne(parsed); + } } public class TestClass From 23556b38ec0ce2548266c90371fc47e918c2b060 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Mon, 24 Mar 2025 15:33:59 +0100 Subject: [PATCH 22/82] Fixed test --- ...dToAggregationExpressionTranslatorTests.cs | 55 ++++++++++++++++++- 1 file changed, 53 insertions(+), 2 deletions(-) diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs index be8be73b41d..dd005919bac 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs @@ -206,6 +206,57 @@ public void MongoDBFunctions_ConvertToLongDataFromIntWithOnErrorAndOnNull_should AssertOutcome(collection, queryable, expectedStages, expectedResult); } + [Theory] + [InlineData(1, null, "FormatException")] + [InlineData(2, "867dee52-c331-484e-92d1-c56479b8e67e", null)] + public void MongoDBFunctions_ConvertToBinDataFromString_should_work(int id, string expectedGuidString, string expectedException) + { + RequireServer.Check().Supports(Feature.ConvertBinDataToFromNumeric); + + var collection = Fixture.Collection; + var queryable = collection.AsQueryable() + .Where(x => x.Id == id) + .Select(x => Mql.ConvertToBinData(x.StringProperty, BsonBinarySubType.UuidStandard, "uuid")); + + var expectedStages = + new[] + { + $"{{ $match : {{ _id : {id} }} }}", + $"{{ $project: {{ _v : {{ $convert : {{ input : '$StringProperty', to : {{ type: 'binData', subtype: 4 }}, format: 'uuid' }} }}, _id : 0 }} }}", + }; + + var expectedResult = expectedGuidString is null? null : new BsonBinaryData(Guid.Parse(expectedGuidString), GuidRepresentation.Standard); + AssertOutcome(collection, queryable, expectedStages, expectedResult, expectedException); + } + + [Theory] + [InlineData(0, "AAAAAAAABMA=", "Ag==", "AAAAAAAABMA=")] + [InlineData(10, "Ag==", "Ag==", "AAAAAAAABMA=")] + public void MongoDBFunctions_ConvertToLongDataFromStringWithOnErrorAndOnNull_should_work(int id, string expectedBase64, string onErrorBase64, string onNullBase64) + { + RequireServer.Check().Supports(Feature.ConvertBinDataToFromNumeric); + + var onErrorBinData = onErrorBase64 == null ? null : new BsonBinaryData(Convert.FromBase64String(onErrorBase64)); + var onNullBinData = onNullBase64 == null ? null : new BsonBinaryData(Convert.FromBase64String(onNullBase64)); + + var collection = Fixture.Collection; + var queryable = collection.AsQueryable() + .Where(x => x.Id == id) + .Select(x => Mql.ConvertToBinData(x.StringProperty, BsonBinarySubType.UuidStandard, "uuid", onErrorBinData, onNullBinData)); + + var onErrorString = onErrorBase64 == null ? "null" : $"BinData(0, '{onErrorBase64}')"; + var onNullString = onNullBase64 == null ? "null" : $"BinData(0, '{onNullBase64}')"; + + var expectedStages = + new[] + { + $"{{ $match : {{ _id : {id} }} }}", + $"{{ $project: {{ _v : {{ $convert : {{ input : '$StringProperty', to : {{ type: 'binData', subtype: 4 }}, onError: {onErrorString}, onNull: {onNullString}, format: 'uuid' }} }}, _id : 0 }} }}", + }; + + var expectedResult = expectedBase64 is null? null : new BsonBinaryData(Convert.FromBase64String(expectedBase64)); + AssertOutcome(collection, queryable, expectedStages, expectedResult); + } // To Double @@ -461,7 +512,7 @@ public sealed class ClassFixture : MongoCollectionFixture [ new TestClass {Id = 0 }, new TestClass {Id = 1, BinaryProperty = new BsonBinaryData([0, 1, 2])}, - new TestClass {Id = 2, BinaryProperty = new BsonBinaryData(Guid.Parse("867dee52-c331-484e-92d1-c56479b8e67e"), GuidRepresentation.Standard)}, + new TestClass {Id = 2, BinaryProperty = new BsonBinaryData(Guid.Parse("867dee52-c331-484e-92d1-c56479b8e67e"), GuidRepresentation.Standard), StringProperty = "867dee52-c331-484e-92d1-c56479b8e67e"}, new TestClass {Id = 3, BinaryProperty = new BsonBinaryData(Convert.FromBase64String("AAAAAAAA4L8=")), DoubleProperty = -0.5}, //LittleEndian new TestClass {Id = 4, BinaryProperty = new BsonBinaryData(Convert.FromBase64String("ogIAAA==")), IntProperty = 674, LongProperty = 674}, //LittleEndian new TestClass {Id = 5, BinaryProperty = new BsonBinaryData(Convert.FromBase64String("wAQAAAAAAAA=")), DoubleProperty = -2.5}, //BigEndian @@ -480,7 +531,7 @@ protected override void InitializeTestCase() _initialed = true; - var errorTestCase = "{ _id: 10, DoubleProperty: NumberDecimal('-32768'), IntProperty: NumberDecimal('-32768'), LongProperty: NumberDecimal('-32768') }"; + const string errorTestCase = "{ _id: 10, DoubleProperty: NumberDecimal('-32768'), IntProperty: NumberDecimal('-32768'), LongProperty: NumberDecimal('-32768'), StringProperty: NumberDecimal('-233') }"; var parsed = BsonDocument.Parse(errorTestCase); var untypedCollection = Database.GetCollection(GetCollectionName()); From 76dd22109bfb4d30d1165b392cea63837ac7f7ff Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Mon, 24 Mar 2025 15:40:44 +0100 Subject: [PATCH 23/82] Various fixes --- .../Ast/AstEnumExtensions.cs | 61 +++++++++++++++++++ .../Ast/Expressions/AstConvertExpression.cs | 13 +--- ...MethodToAggregationExpressionTranslator.cs | 35 ++--------- 3 files changed, 66 insertions(+), 43 deletions(-) create mode 100644 src/MongoDB.Driver/Linq/Linq3Implementation/Ast/AstEnumExtensions.cs diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/AstEnumExtensions.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/AstEnumExtensions.cs new file mode 100644 index 00000000000..e9b6ab86b64 --- /dev/null +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/AstEnumExtensions.cs @@ -0,0 +1,61 @@ +/* Copyright 2010-present MongoDB Inc. + * + * 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; +using MongoDB.Bson; + +namespace MongoDB.Driver.Linq.Linq3Implementation.Ast +{ + internal static class AstEnumExtensions + { + public static string Render(this BsonType type) + { + return type switch + { + BsonType.Array => "array", + BsonType.Binary => "binData", + BsonType.Boolean => "bool", + BsonType.DateTime => "date", + BsonType.Decimal128 => "decimal", + BsonType.Document => "object", + BsonType.Double => "double", + BsonType.Int32 => "int", + BsonType.Int64 => "long", + BsonType.JavaScript => "javascript", + BsonType.JavaScriptWithScope => "javascriptWithScope", + BsonType.MaxKey => "maxKey", + BsonType.MinKey => "minKey", + BsonType.Null => "null", + BsonType.ObjectId => "objectId", + BsonType.RegularExpression => "regex", + BsonType.String => "string", + BsonType.Symbol => "symbol", + BsonType.Timestamp => "timestamp", + BsonType.Undefined => "undefined", + _ => throw new ArgumentException($"Unexpected BSON type: {type}.", nameof(type)) + }; + } + + public static string Render(this Mql.ByteOrder byteOrder) + { + return byteOrder switch + { + Mql.ByteOrder.BigEndian => "big", + Mql.ByteOrder.LittleEndian => "little", + _ => throw new ArgumentException($"Unexpected Mql.ByteOrder: {byteOrder}.", nameof(byteOrder)) + }; + } + } +} \ No newline at end of file diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstConvertExpression.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstConvertExpression.cs index b33217e2b27..1edf94d3fc3 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstConvertExpression.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstConvertExpression.cs @@ -13,7 +13,6 @@ * limitations under the License. */ -using System; using MongoDB.Bson; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Linq.Linq3Implementation.Ast.Visitors; @@ -79,7 +78,7 @@ public override BsonValue Render() { "onError", () => _onError.Render(), _onError != null }, { "onNull", () => _onNull.Render(), _onNull != null }, { "format", () => _format, _format != null}, - { "byteOrder", () => MapMqlByteOrderToString(_byteOrder!.Value), _byteOrder != null} + { "byteOrder", () => _byteOrder!.Value.Render(), _byteOrder != null} } } }; @@ -102,15 +101,5 @@ public AstConvertExpression Update( return new AstConvertExpression(input, to, onError, onNull, subType, format, byteOrder); } - - private static string MapMqlByteOrderToString(Mql.ByteOrder byteOrder) - { - return byteOrder switch - { - Mql.ByteOrder.BigEndian => "big", - Mql.ByteOrder.LittleEndian => "little", - _ => throw new ArgumentException($"Unexpected Mql.ByteOrder: {byteOrder}.", nameof(byteOrder)) - }; - } } } diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs index 884ea6437ae..35b3326b101 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs @@ -6,6 +6,7 @@ using MongoDB.Bson; using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Serializers; +using MongoDB.Driver.Linq.Linq3Implementation.Ast; using MongoDB.Driver.Linq.Linq3Implementation.Ast.Expressions; using MongoDB.Driver.Linq.Linq3Implementation.Reflection; @@ -55,7 +56,7 @@ public static TranslatedExpression Translate(TranslationContext context, MethodC } else { - throw new InvalidOperationException("The 'byteOrder' argument must be a constant expression"); //TODO Improve exception + throw new InvalidOperationException("The 'byteOrder' argument must be a constant expression"); } } @@ -67,40 +68,12 @@ public static TranslatedExpression Translate(TranslationContext context, MethodC } else { - throw new InvalidOperationException("The 'format' argument must be a constant expression"); //TODO Improve exception + throw new InvalidOperationException("The 'format' argument must be a constant expression"); } } - var ast = AstExpression.Convert(fieldAst, MapBsonTypeToString(mapping.Type), onError: onErrorAst, onNull: onNullAst, subType: subTypeAst, format: format, byteOrder: byteOrder); + var ast = AstExpression.Convert(fieldAst, mapping.Type.Render(), onError: onErrorAst, onNull: onNullAst, subType: subTypeAst, format: format, byteOrder: byteOrder); return new TranslatedExpression(expression, ast, mapping.Serializer); } - - private static string MapBsonTypeToString(BsonType type) //TODO need to find a good place for this - { - return type switch - { - BsonType.Array => "array", - BsonType.Binary => "binData", - BsonType.Boolean => "bool", - BsonType.DateTime => "date", - BsonType.Decimal128 => "decimal", - BsonType.Document => "object", - BsonType.Double => "double", - BsonType.Int32 => "int", - BsonType.Int64 => "long", - BsonType.JavaScript => "javascript", - BsonType.JavaScriptWithScope => "javascriptWithScope", - BsonType.MaxKey => "maxKey", - BsonType.MinKey => "minKey", - BsonType.Null => "null", - BsonType.ObjectId => "objectId", - BsonType.RegularExpression => "regex", - BsonType.String => "string", - BsonType.Symbol => "symbol", - BsonType.Timestamp => "timestamp", - BsonType.Undefined => "undefined", - _ => throw new ArgumentException($"Unexpected BSON type: {type}.", nameof(type)) - }; - } } } From 6b6be70a1b7423b37a265490a706704684bc96c2 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Mon, 24 Mar 2025 15:50:29 +0100 Subject: [PATCH 24/82] Naming correction --- .../Reflection/MqlMethod.cs | 36 +++++++++---------- ...essionToAggregationExpressionTranslator.cs | 6 ++-- ...MethodToAggregationExpressionTranslator.cs | 12 +++---- src/MongoDB.Driver/Mql.cs | 12 +++---- ...dToAggregationExpressionTranslatorTests.cs | 12 +++---- 5 files changed, 39 insertions(+), 39 deletions(-) diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/MqlMethod.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/MqlMethod.cs index d56f20ef1df..3ce0d3c42e2 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/MqlMethod.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/MqlMethod.cs @@ -42,12 +42,12 @@ internal static class MqlMethod private static readonly MethodInfo __convertToBinDataFromLongWithOnErrorAndOnNull; private static readonly MethodInfo __convertToBinDataFromString; private static readonly MethodInfo __convertToBinDataFromStringWithOnErrorAndOnNull; - private static readonly MethodInfo __convertToDoubleFromBinData; - private static readonly MethodInfo __convertToDoubleFromBinDataWithOnErrorAndOnNull; - private static readonly MethodInfo __convertToIntFromBinData; - private static readonly MethodInfo __convertToIntFromBinDataWithOnErrorAndOnNull; - private static readonly MethodInfo __convertToLongFromBinData; - private static readonly MethodInfo __convertToLongFromBinDataWithOnErrorAndOnNull; + private static readonly MethodInfo __convertToNullableDoubleFromBinData; + private static readonly MethodInfo __convertToNullableDoubleFromBinDataWithOnErrorAndOnNull; + private static readonly MethodInfo __convertToNullableIntFromBinData; + private static readonly MethodInfo __convertToNullableIntFromBinDataWithOnErrorAndOnNull; + private static readonly MethodInfo __convertToNullableLongFromBinData; + private static readonly MethodInfo __convertToNullableLongFromBinDataWithOnErrorAndOnNull; private static readonly MethodInfo __convertToStringFromBinData; private static readonly MethodInfo __convertToStringFromBinDataWithOnErrorAndOnNull; @@ -79,14 +79,14 @@ static MqlMethod() __convertToBinDataFromStringWithOnErrorAndOnNull = ReflectionInfo.Method((string field, BsonBinarySubType subType, string format, BsonBinaryData onError, BsonBinaryData onNull) => Mql.ConvertToBinData(field, subType, format, onError, onNull)); - __convertToDoubleFromBinData = ReflectionInfo.Method((BsonBinaryData field, Mql.ByteOrder byteOrder) => Mql.ConvertToDouble(field, byteOrder)); - __convertToDoubleFromBinDataWithOnErrorAndOnNull = ReflectionInfo.Method((BsonBinaryData field, Mql.ByteOrder byteOrder, double? onError, double? onNull) => Mql.ConvertToDouble(field, byteOrder, onError, onNull)); + __convertToNullableDoubleFromBinData = ReflectionInfo.Method((BsonBinaryData field, Mql.ByteOrder byteOrder) => Mql.ConvertToNullableDouble(field, byteOrder)); + __convertToNullableDoubleFromBinDataWithOnErrorAndOnNull = ReflectionInfo.Method((BsonBinaryData field, Mql.ByteOrder byteOrder, double? onError, double? onNull) => Mql.ConvertToNullableDouble(field, byteOrder, onError, onNull)); - __convertToIntFromBinData = ReflectionInfo.Method((BsonBinaryData field, Mql.ByteOrder byteOrder) => Mql.ConvertToInt(field, byteOrder)); - __convertToIntFromBinDataWithOnErrorAndOnNull = ReflectionInfo.Method((BsonBinaryData field, Mql.ByteOrder byteOrder, int? onError, int? onNull) => Mql.ConvertToInt(field, byteOrder, onError, onNull)); + __convertToNullableIntFromBinData = ReflectionInfo.Method((BsonBinaryData field, Mql.ByteOrder byteOrder) => Mql.ConvertToNullableInt(field, byteOrder)); + __convertToNullableIntFromBinDataWithOnErrorAndOnNull = ReflectionInfo.Method((BsonBinaryData field, Mql.ByteOrder byteOrder, int? onError, int? onNull) => Mql.ConvertToNullableInt(field, byteOrder, onError, onNull)); - __convertToLongFromBinData = ReflectionInfo.Method((BsonBinaryData field, Mql.ByteOrder byteOrder) => Mql.ConvertToLong(field, byteOrder)); - __convertToLongFromBinDataWithOnErrorAndOnNull = ReflectionInfo.Method((BsonBinaryData field, Mql.ByteOrder byteOrder, long? onError, long? onNull) => Mql.ConvertToLong(field, byteOrder, onError, onNull)); + __convertToNullableLongFromBinData = ReflectionInfo.Method((BsonBinaryData field, Mql.ByteOrder byteOrder) => Mql.ConvertToNullableLong(field, byteOrder)); + __convertToNullableLongFromBinDataWithOnErrorAndOnNull = ReflectionInfo.Method((BsonBinaryData field, Mql.ByteOrder byteOrder, long? onError, long? onNull) => Mql.ConvertToNullableLong(field, byteOrder, onError, onNull)); __convertToStringFromBinData = ReflectionInfo.Method((BsonBinaryData field, string format) => Mql.ConvertToString(field, format)); __convertToStringFromBinDataWithOnErrorAndOnNull = ReflectionInfo.Method((BsonBinaryData field, string format, string onError, string onNull) => Mql.ConvertToString(field, format, onError, onNull)); @@ -111,12 +111,12 @@ static MqlMethod() public static MethodInfo ConvertToBinDataFromLongWithOnErrorAndOnNull => __convertToBinDataFromLongWithOnErrorAndOnNull; public static MethodInfo ConvertToBinDataFromString => __convertToBinDataFromString; public static MethodInfo ConvertToBinDataFromStringWithOnErrorAndOnNull => __convertToBinDataFromStringWithOnErrorAndOnNull; - public static MethodInfo ConvertToDoubleFromBinData => __convertToDoubleFromBinData; - public static MethodInfo ConvertToDoubleFromBinDataWithOnErrorAndOnNull => __convertToDoubleFromBinDataWithOnErrorAndOnNull; - public static MethodInfo ConvertToIntFromBinData => __convertToIntFromBinData; - public static MethodInfo ConvertToIntFromBinDataWithOnErrorAndOnNull => __convertToIntFromBinDataWithOnErrorAndOnNull; - public static MethodInfo ConvertToLongFromBinData => __convertToLongFromBinData; - public static MethodInfo ConvertToLongFromBinDataWithOnErrorAndOnNull => __convertToLongFromBinDataWithOnErrorAndOnNull; + public static MethodInfo ConvertToNullableDoubleFromBinData => __convertToNullableDoubleFromBinData; + public static MethodInfo ConvertToNullableDoubleFromBinDataWithOnErrorAndOnNull => __convertToNullableDoubleFromBinDataWithOnErrorAndOnNull; + public static MethodInfo ConvertToNullableIntFromBinData => __convertToNullableIntFromBinData; + public static MethodInfo ConvertToNullableIntFromBinDataWithOnErrorAndOnNull => __convertToNullableIntFromBinDataWithOnErrorAndOnNull; + public static MethodInfo ConvertToNullableLongFromBinData => __convertToNullableLongFromBinData; + public static MethodInfo ConvertToNullableLongFromBinDataWithOnErrorAndOnNull => __convertToNullableLongFromBinDataWithOnErrorAndOnNull; public static MethodInfo ConvertToStringFromBinData => __convertToStringFromBinData; public static MethodInfo ConvertToStringFromBinDataWithOnErrorAndOnNull => __convertToStringFromBinDataWithOnErrorAndOnNull; } diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodCallExpressionToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodCallExpressionToAggregationExpressionTranslator.cs index 33c12aeeb86..c13d6956842 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodCallExpressionToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodCallExpressionToAggregationExpressionTranslator.cs @@ -139,9 +139,9 @@ public static TranslatedExpression Translate(TranslationContext context, MethodC return CountMethodToAggregationExpressionTranslator.Translate(context, expression); case "ConvertToBinData": - case "ConvertToDouble": - case "ConvertToInt": - case "ConvertToLong": + case "ConvertToNullableDouble": + case "ConvertToNullableInt": + case "ConvertToNullableLong": case "ConvertToString": return ConvertMethodToAggregationExpressionTranslator.Translate(context, expression); diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs index 35b3326b101..d358400ab11 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs @@ -24,12 +24,12 @@ internal class ConvertMethodToAggregationExpressionTranslator BsonBinaryDataSerializer.Instance, BsonType.Binary, null, 1, 2, 3, 4), ([MqlMethod.ConvertToStringFromBinData], StringSerializer.Instance, BsonType.String, 1, null, null, null, null), ([MqlMethod.ConvertToStringFromBinDataWithOnErrorAndOnNull], StringSerializer.Instance, BsonType.String, 1, null, null, 2, 3), - ([MqlMethod.ConvertToIntFromBinData], new NullableSerializer(Int32Serializer.Instance), BsonType.Int32, null, null, 1, null, null), - ([MqlMethod.ConvertToIntFromBinDataWithOnErrorAndOnNull], new NullableSerializer(Int32Serializer.Instance), BsonType.Int32, null, null, 1, 2, 3), - ([MqlMethod.ConvertToLongFromBinData], new NullableSerializer(Int64Serializer.Instance), BsonType.Int64, null, null, 1, null, null), - ([MqlMethod.ConvertToLongFromBinDataWithOnErrorAndOnNull], new NullableSerializer(Int64Serializer.Instance), BsonType.Int64, null, null, 1, 2, 3), - ([MqlMethod.ConvertToDoubleFromBinData], new NullableSerializer(DoubleSerializer.Instance), BsonType.Double, null, null, 1, null, null), - ([MqlMethod.ConvertToDoubleFromBinDataWithOnErrorAndOnNull], new NullableSerializer(DoubleSerializer.Instance), BsonType.Double, null, null, 1, 2, 3) + ([MqlMethod.ConvertToNullableIntFromBinData], new NullableSerializer(Int32Serializer.Instance), BsonType.Int32, null, null, 1, null, null), + ([MqlMethod.ConvertToNullableIntFromBinDataWithOnErrorAndOnNull], new NullableSerializer(Int32Serializer.Instance), BsonType.Int32, null, null, 1, 2, 3), + ([MqlMethod.ConvertToNullableLongFromBinData], new NullableSerializer(Int64Serializer.Instance), BsonType.Int64, null, null, 1, null, null), + ([MqlMethod.ConvertToNullableLongFromBinDataWithOnErrorAndOnNull], new NullableSerializer(Int64Serializer.Instance), BsonType.Int64, null, null, 1, 2, 3), + ([MqlMethod.ConvertToNullableDoubleFromBinData], new NullableSerializer(DoubleSerializer.Instance), BsonType.Double, null, null, 1, null, null), + ([MqlMethod.ConvertToNullableDoubleFromBinDataWithOnErrorAndOnNull], new NullableSerializer(DoubleSerializer.Instance), BsonType.Double, null, null, 1, 2, 3) ]; public static TranslatedExpression Translate(TranslationContext context, MethodCallExpression expression) { diff --git a/src/MongoDB.Driver/Mql.cs b/src/MongoDB.Driver/Mql.cs index 7c409f022bf..b4f108f15f4 100644 --- a/src/MongoDB.Driver/Mql.cs +++ b/src/MongoDB.Driver/Mql.cs @@ -190,7 +190,7 @@ public static string ConvertToString(BsonBinaryData value, string format, string /// The value. /// The byte ordering of BsonBinaryData. /// The converted value. - public static int? ConvertToInt(BsonBinaryData value, ByteOrder byteOrder) + public static int? ConvertToNullableInt(BsonBinaryData value, ByteOrder byteOrder) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } @@ -203,7 +203,7 @@ public static string ConvertToString(BsonBinaryData value, string format, string /// The onError value. /// The onNull value. /// The converted value. - public static int? ConvertToInt(BsonBinaryData value, ByteOrder byteOrder, int? onError, int? onNull) + public static int? ConvertToNullableInt(BsonBinaryData value, ByteOrder byteOrder, int? onError, int? onNull) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } @@ -214,7 +214,7 @@ public static string ConvertToString(BsonBinaryData value, string format, string /// The value. /// The byte ordering of BsonBinaryData. /// The converted value. - public static long? ConvertToLong(BsonBinaryData value, ByteOrder byteOrder) + public static long? ConvertToNullableLong(BsonBinaryData value, ByteOrder byteOrder) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } @@ -227,7 +227,7 @@ public static string ConvertToString(BsonBinaryData value, string format, string /// The onError value. /// The onNull value. /// The converted value. - public static long? ConvertToLong(BsonBinaryData value, ByteOrder byteOrder, long? onError, long? onNull) + public static long? ConvertToNullableLong(BsonBinaryData value, ByteOrder byteOrder, long? onError, long? onNull) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } @@ -238,7 +238,7 @@ public static string ConvertToString(BsonBinaryData value, string format, string /// The value. /// The byte ordering of BsonBinaryData. /// The converted value. - public static double? ConvertToDouble(BsonBinaryData value, ByteOrder byteOrder) + public static double? ConvertToNullableDouble(BsonBinaryData value, ByteOrder byteOrder) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } @@ -251,7 +251,7 @@ public static string ConvertToString(BsonBinaryData value, string format, string /// The onError value. /// The onNull value. /// The converted value. - public static double? ConvertToDouble(BsonBinaryData value, ByteOrder byteOrder, double? onError, double? onNull) + public static double? ConvertToNullableDouble(BsonBinaryData value, ByteOrder byteOrder, double? onError, double? onNull) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs index dd005919bac..6d449dd2090 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs @@ -271,7 +271,7 @@ public void MongoDBFunctions_ConvertToDoubleFromBinData_should_work(int id, Mql. var collection = Fixture.Collection; var queryable = collection.AsQueryable() .Where(x => x.Id == id) - .Select(x => Mql.ConvertToDouble(x.BinaryProperty, byteOrder)); + .Select(x => Mql.ConvertToNullableDouble(x.BinaryProperty, byteOrder)); var expectedStages = new[] @@ -297,7 +297,7 @@ public void MongoDBFunctions_ConvertToDoubleFromBinDataWithOnErrorAndOnNull_shou var collection = Fixture.Collection; var queryable = collection.AsQueryable() .Where(x => x.Id == id) - .Select(x => Mql.ConvertToDouble(x.BinaryProperty, byteOrder, onError, onNull)); + .Select(x => Mql.ConvertToNullableDouble(x.BinaryProperty, byteOrder, onError, onNull)); var onErrorString = onError == null ? "null" : onError.Value.ToString(NumberFormatInfo.InvariantInfo); var onNullString = onNull == null ? "null" : onNull.Value.ToString(NumberFormatInfo.InvariantInfo); @@ -324,7 +324,7 @@ public void MongoDBFunctions_ConvertToIntFromBinData_should_work(int id, Mql.Byt var collection = Fixture.Collection; var queryable = collection.AsQueryable() .Where(x => x.Id == id) - .Select(x => Mql.ConvertToInt(x.BinaryProperty, byteOrder)); + .Select(x => Mql.ConvertToNullableInt(x.BinaryProperty, byteOrder)); var expectedStages = new[] @@ -350,7 +350,7 @@ public void MongoDBFunctions_ConvertToIntFromBinDataWithOnErrorAndOnNull_should_ var collection = Fixture.Collection; var queryable = collection.AsQueryable() .Where(x => x.Id == id) - .Select(x => Mql.ConvertToInt(x.BinaryProperty, byteOrder, onError, onNull)); + .Select(x => Mql.ConvertToNullableInt(x.BinaryProperty, byteOrder, onError, onNull)); var onErrorString = onError == null ? "null" : onError.Value.ToString(NumberFormatInfo.InvariantInfo); var onNullString = onNull == null ? "null" : onNull.Value.ToString(NumberFormatInfo.InvariantInfo); @@ -377,7 +377,7 @@ public void MongoDBFunctions_ConvertToLongFromBinData_should_work(int id, Mql.By var collection = Fixture.Collection; var queryable = collection.AsQueryable() .Where(x => x.Id == id) - .Select(x => Mql.ConvertToLong(x.BinaryProperty, byteOrder)); + .Select(x => Mql.ConvertToNullableLong(x.BinaryProperty, byteOrder)); var expectedStages = new[] @@ -403,7 +403,7 @@ public void MongoDBFunctions_ConvertToLongFromBinDataWithOnErrorAndOnNull_should var collection = Fixture.Collection; var queryable = collection.AsQueryable() .Where(x => x.Id == id) - .Select(x => Mql.ConvertToLong(x.BinaryProperty, byteOrder, onError, onNull)); + .Select(x => Mql.ConvertToNullableLong(x.BinaryProperty, byteOrder, onError, onNull)); var onErrorString = onError == null ? "null" : onError.Value.ToString(NumberFormatInfo.InvariantInfo); var onNullString = onNull == null ? "null" : onNull.Value.ToString(NumberFormatInfo.InvariantInfo); From 311832d65cf01afc829021239415094017ced460 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Mon, 24 Mar 2025 15:58:00 +0100 Subject: [PATCH 25/82] Small naming correction --- ...tMethodToAggregationExpressionTranslatorTests.cs | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs index 6d449dd2090..37cee4e6764 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs @@ -506,8 +506,8 @@ private string ByteOrderToString(Mql.ByteOrder byteOrder) public sealed class ClassFixture : MongoCollectionFixture { + private bool _initialized; - private bool _initialed = false; protected override IEnumerable InitialData { get; } = [ new TestClass {Id = 0 }, @@ -517,25 +517,22 @@ public sealed class ClassFixture : MongoCollectionFixture new TestClass {Id = 4, BinaryProperty = new BsonBinaryData(Convert.FromBase64String("ogIAAA==")), IntProperty = 674, LongProperty = 674}, //LittleEndian new TestClass {Id = 5, BinaryProperty = new BsonBinaryData(Convert.FromBase64String("wAQAAAAAAAA=")), DoubleProperty = -2.5}, //BigEndian new TestClass {Id = 6, BinaryProperty = new BsonBinaryData(Convert.FromBase64String("AAAAKg==")), IntProperty = 42, LongProperty = 42}, //BigEndian - new TestClass {Id = 7, BinaryProperty = new BsonBinaryData(Convert.FromBase64String("AQCAfwA="))}, //5 byte single precision double should error //TODO Maybe we don't use this ]; protected override void InitializeTestCase() { base.InitializeTestCase(); - if (_initialed) + if (_initialized) { return; } - _initialed = true; - - const string errorTestCase = "{ _id: 10, DoubleProperty: NumberDecimal('-32768'), IntProperty: NumberDecimal('-32768'), LongProperty: NumberDecimal('-32768'), StringProperty: NumberDecimal('-233') }"; - var parsed = BsonDocument.Parse(errorTestCase); + _initialized = true; + //It is not possible to create a $convert that causes an error when converting int/long/double to binData using the typed API. Converting 'decimal' to binData is not allowed. + var parsed = BsonDocument.Parse("{ _id: 10, DoubleProperty: NumberDecimal('-32768'), IntProperty: NumberDecimal('-32768'), LongProperty: NumberDecimal('-32768'), StringProperty: NumberDecimal('-233') }"); var untypedCollection = Database.GetCollection(GetCollectionName()); - untypedCollection.InsertOne(parsed); } } From 34ef476f971102809015b4b8235cbbd362980931 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Mon, 24 Mar 2025 16:08:03 +0100 Subject: [PATCH 26/82] Small corrections --- src/MongoDB.Driver/Core/Misc/Feature.cs | 6 ++++++ ...ConvertMethodToAggregationExpressionTranslatorTests.cs | 8 ++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/MongoDB.Driver/Core/Misc/Feature.cs b/src/MongoDB.Driver/Core/Misc/Feature.cs index a1a14aa90cf..35496cc378b 100644 --- a/src/MongoDB.Driver/Core/Misc/Feature.cs +++ b/src/MongoDB.Driver/Core/Misc/Feature.cs @@ -45,6 +45,7 @@ public class Feature private static readonly Feature __clientSideEncryption = new Feature("ClientSideEncryption", WireVersion.Server42); private static readonly Feature __clusteredIndexes = new Feature("ClusteredIndexes", WireVersion.Server53); private static readonly Feature __convertBinDataToFromNumeric = new Feature("ConvertBinDataToFromNumeric", WireVersion.Server81); + private static readonly Feature __convertBinDataToFromString= new Feature("ConvertBinDataToFromString", WireVersion.Server80); private static readonly Feature __createIndexCommitQuorum = new Feature("CreateIndexCommitQuorum", WireVersion.Server44); private static readonly Feature __createIndexesUsingInsertOperations = new Feature("CreateIndexesUsingInsertOperations", WireVersion.Zero, WireVersion.Server42); private static readonly Feature __csfleRangeAlgorithm = new Feature("CsfleRangeAlgorithm", WireVersion.Server62); @@ -199,6 +200,11 @@ public class Feature /// public static Feature ConvertBinDataToFromNumeric => __convertBinDataToFromNumeric; + /// + /// Gets the conversion of binary data to/from string feature. + /// + public static Feature ConvertBinDataToFromString => __convertBinDataToFromString; + /// /// Gets the create index commit quorum feature. /// diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs index 37cee4e6764..eeed4bfe624 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs @@ -211,7 +211,7 @@ public void MongoDBFunctions_ConvertToLongDataFromIntWithOnErrorAndOnNull_should [InlineData(2, "867dee52-c331-484e-92d1-c56479b8e67e", null)] public void MongoDBFunctions_ConvertToBinDataFromString_should_work(int id, string expectedGuidString, string expectedException) { - RequireServer.Check().Supports(Feature.ConvertBinDataToFromNumeric); + RequireServer.Check().Supports(Feature.ConvertBinDataToFromString); var collection = Fixture.Collection; var queryable = collection.AsQueryable() @@ -232,7 +232,7 @@ public void MongoDBFunctions_ConvertToBinDataFromString_should_work(int id, stri [Theory] [InlineData(0, "AAAAAAAABMA=", "Ag==", "AAAAAAAABMA=")] [InlineData(10, "Ag==", "Ag==", "AAAAAAAABMA=")] - public void MongoDBFunctions_ConvertToLongDataFromStringWithOnErrorAndOnNull_should_work(int id, string expectedBase64, string onErrorBase64, string onNullBase64) + public void MongoDBFunctions_ConvertToBinDataFromStringWithOnErrorAndOnNull_should_work(int id, string expectedBase64, string onErrorBase64, string onNullBase64) { RequireServer.Check().Supports(Feature.ConvertBinDataToFromNumeric); @@ -424,7 +424,7 @@ public void MongoDBFunctions_ConvertToLongFromBinDataWithOnErrorAndOnNull_should [InlineData(1, null, "MongoCommandException")] public void MongoDBFunctions_ConvertToStringFromBinData_should_work(int id, string expectedResult, string expectedException) { - RequireServer.Check().Supports(Feature.ConvertBinDataToFromNumeric); + RequireServer.Check().Supports(Feature.ConvertBinDataToFromString); var collection = Fixture.Collection; var queryable = collection.AsQueryable() @@ -448,7 +448,7 @@ public void MongoDBFunctions_ConvertToStringFromBinData_should_work(int id, stri [InlineData(1, null, null, "onNull")] public void MongoDBFunctions_ConvertToStringFromBinDataWithOnErrorAndOnNull_should_work(int id, string expectedResult, string onError, string onNull) { - RequireServer.Check().Supports(Feature.ConvertBinDataToFromNumeric); + RequireServer.Check().Supports(Feature.ConvertBinDataToFromString); var collection = Fixture.Collection; var queryable = collection.AsQueryable() From 5f1732fbac3be6abe310c6f2f70aa32dfad52ecd Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Tue, 25 Mar 2025 10:56:11 +0100 Subject: [PATCH 27/82] Fixes according to PR --- .../Ast/Expressions/AstConvertExpression.cs | 26 ++++++++----------- .../Ast/Expressions/AstExpression.cs | 4 +-- .../Ast/Visitors/AstNodeVisitor.cs | 3 +-- ...MethodToAggregationExpressionTranslator.cs | 15 ++++++++++- 4 files changed, 28 insertions(+), 20 deletions(-) diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstConvertExpression.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstConvertExpression.cs index 1edf94d3fc3..1d05d91d5f3 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstConvertExpression.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstConvertExpression.cs @@ -21,22 +21,22 @@ namespace MongoDB.Driver.Linq.Linq3Implementation.Ast.Expressions { internal sealed class AstConvertExpression : AstExpression { + private readonly Mql.ByteOrder? _byteOrder; private readonly AstExpression _input; private readonly string _format; private readonly AstExpression _onError; private readonly AstExpression _onNull; + private readonly BsonBinarySubType? _subType; private readonly AstExpression _to; - private readonly AstExpression _subType; - private readonly Mql.ByteOrder? _byteOrder; public AstConvertExpression( AstExpression input, AstExpression to, - AstExpression onError = null, - AstExpression onNull = null, - AstExpression subType = null, + BsonBinarySubType? subType = null, + Mql.ByteOrder? byteOrder = null, string format = null, - Mql.ByteOrder? byteOrder = null) + AstExpression onError = null, + AstExpression onNull = null) { _input = Ensure.IsNotNull(input, nameof(input)); _to = Ensure.IsNotNull(to, nameof(to)); @@ -53,8 +53,8 @@ public AstConvertExpression( public override AstNodeType NodeType => AstNodeType.ConvertExpression; public AstExpression OnError => _onError; public AstExpression OnNull => _onNull; + public BsonBinarySubType? SubType => _subType; public AstExpression To => _to; - public AstExpression SubType => _subType; public override AstNode Accept(AstNodeVisitor visitor) { @@ -72,7 +72,7 @@ public override BsonValue Render() { "to", () => new BsonDocument { {"type", _to.Render() }, - {"subtype", _subType.Render()}, + {"subtype", (int)_subType!.Value}, }, _subType != null }, { "onError", () => _onError.Render(), _onError != null }, @@ -88,18 +88,14 @@ public AstConvertExpression Update( AstExpression input, AstExpression to, AstExpression onError, - AstExpression onNull, - AstExpression subType, - string format, - Mql.ByteOrder? byteOrder) + AstExpression onNull) { - if (input == _input && to == _to && onError == _onError && onNull == _onNull && - subType == _subType && format == _format && byteOrder == _byteOrder) + if (input == _input && to == _to && onError == _onError && onNull == _onNull) { return this; } - return new AstConvertExpression(input, to, onError, onNull, subType, format, byteOrder); + return new AstConvertExpression(input, to, _subType, _byteOrder, _format, onError, onNull); } } } diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstExpression.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstExpression.cs index dbc1430f7ea..59e489a97bc 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstExpression.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstExpression.cs @@ -256,7 +256,7 @@ public static AstExpression Constant(BsonValue value) } public static AstExpression Convert(AstExpression input, AstExpression to, AstExpression onError = null, AstExpression onNull = null, - AstExpression subType = null, string format = null, Mql.ByteOrder? byteOrder = null) + BsonBinarySubType? subType = null, string format = null, Mql.ByteOrder? byteOrder = null) { Ensure.IsNotNull(input, nameof(input)); Ensure.IsNotNull(to, nameof(to)); @@ -284,7 +284,7 @@ to is AstConstantExpression toConstantExpression && } } - return new AstConvertExpression(input, to, onError, onNull, subType, format, byteOrder); + return new AstConvertExpression(input, to, subType, byteOrder, format, onError, onNull); } public static AstExpression DateAdd( diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Visitors/AstNodeVisitor.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Visitors/AstNodeVisitor.cs index bf9acb51cce..1222d0060e9 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Visitors/AstNodeVisitor.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Visitors/AstNodeVisitor.cs @@ -226,8 +226,7 @@ public virtual AstNode VisitConstantExpression(AstConstantExpression node) public virtual AstNode VisitConvertExpression(AstConvertExpression node) { - return node.Update(VisitAndConvert(node.Input), VisitAndConvert(node.To), VisitAndConvert(node.OnError), VisitAndConvert(node.OnNull), - VisitAndConvert(node.SubType), node.Format, node.ByteOrder); + return node.Update(VisitAndConvert(node.Input), VisitAndConvert(node.To), VisitAndConvert(node.OnError), VisitAndConvert(node.OnNull)); } public virtual AstNode VisitCountStage(AstCountStage node) diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs index d358400ab11..250f3c878c8 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs @@ -41,6 +41,7 @@ public static TranslatedExpression Translate(TranslationContext context, MethodC throw new ExpressionNotSupportedException(expression); Mql.ByteOrder? byteOrder = null; + BsonBinarySubType? subType = null; string format = null; var fieldAst = ExpressionToAggregationExpressionTranslator.Translate(context, arguments[0]).Ast; @@ -72,7 +73,19 @@ public static TranslatedExpression Translate(TranslationContext context, MethodC } } - var ast = AstExpression.Convert(fieldAst, mapping.Type.Render(), onError: onErrorAst, onNull: onNullAst, subType: subTypeAst, format: format, byteOrder: byteOrder); + if (mapping.SubTypeIndex.HasValue) + { + if (arguments[mapping.SubTypeIndex.Value] is ConstantExpression co) + { + subType = (BsonBinarySubType)co.Value!; + } + else + { + throw new InvalidOperationException("The 'subType' argument must be a constant expression"); + } + } + + var ast = AstExpression.Convert(fieldAst, mapping.Type.Render(), onError: onErrorAst, onNull: onNullAst, subType: subType, format: format, byteOrder: byteOrder); return new TranslatedExpression(expression, ast, mapping.Serializer); } } From b17c354fbba091503d487b03807ba8e70a5f9fa8 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Tue, 25 Mar 2025 11:01:03 +0100 Subject: [PATCH 28/82] Moved byteOrder to outer scope --- .../Ast/AstEnumExtensions.cs | 6 +- .../Ast/Expressions/AstConvertExpression.cs | 6 +- .../Ast/Expressions/AstExpression.cs | 2 +- .../Reflection/MqlMethod.cs | 24 ++-- ...MethodToAggregationExpressionTranslator.cs | 4 +- src/MongoDB.Driver/Mql.cs | 24 ++-- ...dToAggregationExpressionTranslatorTests.cs | 128 +++++++++--------- 7 files changed, 97 insertions(+), 97 deletions(-) diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/AstEnumExtensions.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/AstEnumExtensions.cs index e9b6ab86b64..9f24e8e7ff8 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/AstEnumExtensions.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/AstEnumExtensions.cs @@ -48,12 +48,12 @@ public static string Render(this BsonType type) }; } - public static string Render(this Mql.ByteOrder byteOrder) + public static string Render(this ByteOrder byteOrder) { return byteOrder switch { - Mql.ByteOrder.BigEndian => "big", - Mql.ByteOrder.LittleEndian => "little", + ByteOrder.BigEndian => "big", + ByteOrder.LittleEndian => "little", _ => throw new ArgumentException($"Unexpected Mql.ByteOrder: {byteOrder}.", nameof(byteOrder)) }; } diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstConvertExpression.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstConvertExpression.cs index 1d05d91d5f3..95a31218ecd 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstConvertExpression.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstConvertExpression.cs @@ -21,7 +21,7 @@ namespace MongoDB.Driver.Linq.Linq3Implementation.Ast.Expressions { internal sealed class AstConvertExpression : AstExpression { - private readonly Mql.ByteOrder? _byteOrder; + private readonly ByteOrder? _byteOrder; private readonly AstExpression _input; private readonly string _format; private readonly AstExpression _onError; @@ -33,7 +33,7 @@ public AstConvertExpression( AstExpression input, AstExpression to, BsonBinarySubType? subType = null, - Mql.ByteOrder? byteOrder = null, + ByteOrder? byteOrder = null, string format = null, AstExpression onError = null, AstExpression onNull = null) @@ -47,7 +47,7 @@ public AstConvertExpression( _byteOrder = byteOrder; } - public Mql.ByteOrder? ByteOrder => _byteOrder; + public ByteOrder? ByteOrder => _byteOrder; public AstExpression Input => _input; public string Format => _format; public override AstNodeType NodeType => AstNodeType.ConvertExpression; diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstExpression.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstExpression.cs index 59e489a97bc..c7d9973ec1a 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstExpression.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstExpression.cs @@ -256,7 +256,7 @@ public static AstExpression Constant(BsonValue value) } public static AstExpression Convert(AstExpression input, AstExpression to, AstExpression onError = null, AstExpression onNull = null, - BsonBinarySubType? subType = null, string format = null, Mql.ByteOrder? byteOrder = null) + BsonBinarySubType? subType = null, string format = null, ByteOrder? byteOrder = null) { Ensure.IsNotNull(input, nameof(input)); Ensure.IsNotNull(to, nameof(to)); diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/MqlMethod.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/MqlMethod.cs index 3ce0d3c42e2..5178b178d4b 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/MqlMethod.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/MqlMethod.cs @@ -66,27 +66,27 @@ static MqlMethod() __isNullOrMissing = ReflectionInfo.Method((object field) => Mql.IsNullOrMissing(field)); // Convert methods - __convertToBinDataFromDouble = ReflectionInfo.Method((double? field, BsonBinarySubType subType, Mql.ByteOrder byteOrder) => Mql.ConvertToBinData(field, subType, byteOrder)); - __convertToBinDataFromDoubleWithOnErrorAndOnNull = ReflectionInfo.Method((double? field, BsonBinarySubType subType, Mql.ByteOrder byteOrder, BsonBinaryData onError, BsonBinaryData onNull) + __convertToBinDataFromDouble = ReflectionInfo.Method((double? field, BsonBinarySubType subType, ByteOrder byteOrder) => Mql.ConvertToBinData(field, subType, byteOrder)); + __convertToBinDataFromDoubleWithOnErrorAndOnNull = ReflectionInfo.Method((double? field, BsonBinarySubType subType, ByteOrder byteOrder, BsonBinaryData onError, BsonBinaryData onNull) => Mql.ConvertToBinData(field, subType, byteOrder, onError, onNull)); - __convertToBinDataFromInt = ReflectionInfo.Method((int? field, BsonBinarySubType subType, Mql.ByteOrder byteOrder) => Mql.ConvertToBinData(field, subType, byteOrder)); - __convertToBinDataFromIntWithOnErrorAndOnNull = ReflectionInfo.Method((int? field, BsonBinarySubType subType, Mql.ByteOrder byteOrder, BsonBinaryData onError, BsonBinaryData onNull) + __convertToBinDataFromInt = ReflectionInfo.Method((int? field, BsonBinarySubType subType, ByteOrder byteOrder) => Mql.ConvertToBinData(field, subType, byteOrder)); + __convertToBinDataFromIntWithOnErrorAndOnNull = ReflectionInfo.Method((int? field, BsonBinarySubType subType, ByteOrder byteOrder, BsonBinaryData onError, BsonBinaryData onNull) => Mql.ConvertToBinData(field, subType, byteOrder, onError, onNull)); - __convertToBinDataFromLong = ReflectionInfo.Method((long? field, BsonBinarySubType subType, Mql.ByteOrder byteOrder) => Mql.ConvertToBinData(field, subType, byteOrder)); - __convertToBinDataFromLongWithOnErrorAndOnNull = ReflectionInfo.Method((long? field, BsonBinarySubType subType, Mql.ByteOrder byteOrder, BsonBinaryData onError, BsonBinaryData onNull) + __convertToBinDataFromLong = ReflectionInfo.Method((long? field, BsonBinarySubType subType, ByteOrder byteOrder) => Mql.ConvertToBinData(field, subType, byteOrder)); + __convertToBinDataFromLongWithOnErrorAndOnNull = ReflectionInfo.Method((long? field, BsonBinarySubType subType, ByteOrder byteOrder, BsonBinaryData onError, BsonBinaryData onNull) => Mql.ConvertToBinData(field, subType, byteOrder, onError, onNull)); __convertToBinDataFromString = ReflectionInfo.Method((string field, BsonBinarySubType subType, string format) => Mql.ConvertToBinData(field, subType, format)); __convertToBinDataFromStringWithOnErrorAndOnNull = ReflectionInfo.Method((string field, BsonBinarySubType subType, string format, BsonBinaryData onError, BsonBinaryData onNull) => Mql.ConvertToBinData(field, subType, format, onError, onNull)); - __convertToNullableDoubleFromBinData = ReflectionInfo.Method((BsonBinaryData field, Mql.ByteOrder byteOrder) => Mql.ConvertToNullableDouble(field, byteOrder)); - __convertToNullableDoubleFromBinDataWithOnErrorAndOnNull = ReflectionInfo.Method((BsonBinaryData field, Mql.ByteOrder byteOrder, double? onError, double? onNull) => Mql.ConvertToNullableDouble(field, byteOrder, onError, onNull)); + __convertToNullableDoubleFromBinData = ReflectionInfo.Method((BsonBinaryData field, ByteOrder byteOrder) => Mql.ConvertToNullableDouble(field, byteOrder)); + __convertToNullableDoubleFromBinDataWithOnErrorAndOnNull = ReflectionInfo.Method((BsonBinaryData field, ByteOrder byteOrder, double? onError, double? onNull) => Mql.ConvertToNullableDouble(field, byteOrder, onError, onNull)); - __convertToNullableIntFromBinData = ReflectionInfo.Method((BsonBinaryData field, Mql.ByteOrder byteOrder) => Mql.ConvertToNullableInt(field, byteOrder)); - __convertToNullableIntFromBinDataWithOnErrorAndOnNull = ReflectionInfo.Method((BsonBinaryData field, Mql.ByteOrder byteOrder, int? onError, int? onNull) => Mql.ConvertToNullableInt(field, byteOrder, onError, onNull)); + __convertToNullableIntFromBinData = ReflectionInfo.Method((BsonBinaryData field, ByteOrder byteOrder) => Mql.ConvertToNullableInt(field, byteOrder)); + __convertToNullableIntFromBinDataWithOnErrorAndOnNull = ReflectionInfo.Method((BsonBinaryData field, ByteOrder byteOrder, int? onError, int? onNull) => Mql.ConvertToNullableInt(field, byteOrder, onError, onNull)); - __convertToNullableLongFromBinData = ReflectionInfo.Method((BsonBinaryData field, Mql.ByteOrder byteOrder) => Mql.ConvertToNullableLong(field, byteOrder)); - __convertToNullableLongFromBinDataWithOnErrorAndOnNull = ReflectionInfo.Method((BsonBinaryData field, Mql.ByteOrder byteOrder, long? onError, long? onNull) => Mql.ConvertToNullableLong(field, byteOrder, onError, onNull)); + __convertToNullableLongFromBinData = ReflectionInfo.Method((BsonBinaryData field, ByteOrder byteOrder) => Mql.ConvertToNullableLong(field, byteOrder)); + __convertToNullableLongFromBinDataWithOnErrorAndOnNull = ReflectionInfo.Method((BsonBinaryData field, ByteOrder byteOrder, long? onError, long? onNull) => Mql.ConvertToNullableLong(field, byteOrder, onError, onNull)); __convertToStringFromBinData = ReflectionInfo.Method((BsonBinaryData field, string format) => Mql.ConvertToString(field, format)); __convertToStringFromBinDataWithOnErrorAndOnNull = ReflectionInfo.Method((BsonBinaryData field, string format, string onError, string onNull) => Mql.ConvertToString(field, format, onError, onNull)); diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs index 250f3c878c8..4e6422a20cd 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs @@ -40,7 +40,7 @@ public static TranslatedExpression Translate(TranslationContext context, MethodC if (mapping == default) throw new ExpressionNotSupportedException(expression); - Mql.ByteOrder? byteOrder = null; + ByteOrder? byteOrder = null; BsonBinarySubType? subType = null; string format = null; @@ -53,7 +53,7 @@ public static TranslatedExpression Translate(TranslationContext context, MethodC { if (arguments[mapping.ByteOrderIndex.Value] is ConstantExpression co) { - byteOrder = (Mql.ByteOrder)co.Value!; + byteOrder = (ByteOrder)co.Value!; } else { diff --git a/src/MongoDB.Driver/Mql.cs b/src/MongoDB.Driver/Mql.cs index b4f108f15f4..1250b629bcc 100644 --- a/src/MongoDB.Driver/Mql.cs +++ b/src/MongoDB.Driver/Mql.cs @@ -359,20 +359,20 @@ public static bool IsNullOrMissing(TField field) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } + } + /// + /// Represents the byte order of binData when converting to/from numerical types. + /// + public enum ByteOrder + { /// - /// + /// Big endian order. /// - public enum ByteOrder - { - /// - /// - /// - BigEndian, - /// - /// - /// - LittleEndian, - } + BigEndian, + /// + /// Little endian order. + /// + LittleEndian, } } diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs index eeed4bfe624..dbf63e7a79c 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs @@ -37,10 +37,10 @@ public ConvertMethodToAggregationExpressionTranslatorTests(ClassFixture fixture) // To BinData [Theory] - [InlineData(1, Mql.ByteOrder.BigEndian, null, "FormatException")] - [InlineData(3, Mql.ByteOrder.LittleEndian,"AAAAAAAA4L8=", null)] - [InlineData(5, Mql.ByteOrder.BigEndian, "wAQAAAAAAAA=", null )] - public void MongoDBFunctions_ConvertToBinDataFromDouble_should_work(int id, Mql.ByteOrder byteOrder, string expectedBase64, string expectedException) + [InlineData(1, ByteOrder.BigEndian, null, "FormatException")] + [InlineData(3, ByteOrder.LittleEndian,"AAAAAAAA4L8=", null)] + [InlineData(5, ByteOrder.BigEndian, "wAQAAAAAAAA=", null )] + public void MongoDBFunctions_ConvertToBinDataFromDouble_should_work(int id, ByteOrder byteOrder, string expectedBase64, string expectedException) { RequireServer.Check().Supports(Feature.ConvertBinDataToFromNumeric); @@ -61,11 +61,11 @@ public void MongoDBFunctions_ConvertToBinDataFromDouble_should_work(int id, Mql. } [Theory] - [InlineData(0, Mql.ByteOrder.LittleEndian, "AAAAAAAABMA=", "Ag==", "AAAAAAAABMA=")] - [InlineData(10, Mql.ByteOrder.LittleEndian, "Ag==", "Ag==", "AAAAAAAABMA=")] - [InlineData(0, Mql.ByteOrder.BigEndian, "AAAAAAAABMA=", "Ag==", "AAAAAAAABMA=")] - [InlineData(10, Mql.ByteOrder.BigEndian, "Ag==", "Ag==", "AAAAAAAABMA=")] - public void MongoDBFunctions_ConvertToBinDataFromDoubleWithOnErrorAndOnNull_should_work(int id, Mql.ByteOrder byteOrder, string expectedBase64, string onErrorBase64, string onNullBase64) + [InlineData(0, ByteOrder.LittleEndian, "AAAAAAAABMA=", "Ag==", "AAAAAAAABMA=")] + [InlineData(10, ByteOrder.LittleEndian, "Ag==", "Ag==", "AAAAAAAABMA=")] + [InlineData(0, ByteOrder.BigEndian, "AAAAAAAABMA=", "Ag==", "AAAAAAAABMA=")] + [InlineData(10, ByteOrder.BigEndian, "Ag==", "Ag==", "AAAAAAAABMA=")] + public void MongoDBFunctions_ConvertToBinDataFromDoubleWithOnErrorAndOnNull_should_work(int id, ByteOrder byteOrder, string expectedBase64, string onErrorBase64, string onNullBase64) { RequireServer.Check().Supports(Feature.ConvertBinDataToFromNumeric); @@ -91,10 +91,10 @@ public void MongoDBFunctions_ConvertToBinDataFromDoubleWithOnErrorAndOnNull_shou } [Theory] - [InlineData(1, Mql.ByteOrder.BigEndian, null, "FormatException")] - [InlineData(4, Mql.ByteOrder.LittleEndian,"ogIAAA==", null)] - [InlineData(6, Mql.ByteOrder.BigEndian, "AAAAKg==", null )] - public void MongoDBFunctions_ConvertToBinDataFromInt_should_work(int id, Mql.ByteOrder byteOrder, string expectedBase64, string expectedException) + [InlineData(1, ByteOrder.BigEndian, null, "FormatException")] + [InlineData(4, ByteOrder.LittleEndian,"ogIAAA==", null)] + [InlineData(6, ByteOrder.BigEndian, "AAAAKg==", null )] + public void MongoDBFunctions_ConvertToBinDataFromInt_should_work(int id, ByteOrder byteOrder, string expectedBase64, string expectedException) { RequireServer.Check().Supports(Feature.ConvertBinDataToFromNumeric); @@ -115,11 +115,11 @@ public void MongoDBFunctions_ConvertToBinDataFromInt_should_work(int id, Mql.Byt } [Theory] - [InlineData(0, Mql.ByteOrder.LittleEndian, "AAAAAAAABMA=", "Ag==", "AAAAAAAABMA=")] - [InlineData(10, Mql.ByteOrder.LittleEndian, "Ag==", "Ag==", "AAAAAAAABMA=")] - [InlineData(0, Mql.ByteOrder.BigEndian, "AAAAAAAABMA=", "Ag==", "AAAAAAAABMA=")] - [InlineData(10, Mql.ByteOrder.BigEndian, "Ag==", "Ag==", "AAAAAAAABMA=")] - public void MongoDBFunctions_ConvertToBinDataFromIntWithOnErrorAndOnNull_should_work(int id, Mql.ByteOrder byteOrder, string expectedBase64, string onErrorBase64, string onNullBase64) + [InlineData(0, ByteOrder.LittleEndian, "AAAAAAAABMA=", "Ag==", "AAAAAAAABMA=")] + [InlineData(10, ByteOrder.LittleEndian, "Ag==", "Ag==", "AAAAAAAABMA=")] + [InlineData(0, ByteOrder.BigEndian, "AAAAAAAABMA=", "Ag==", "AAAAAAAABMA=")] + [InlineData(10, ByteOrder.BigEndian, "Ag==", "Ag==", "AAAAAAAABMA=")] + public void MongoDBFunctions_ConvertToBinDataFromIntWithOnErrorAndOnNull_should_work(int id, ByteOrder byteOrder, string expectedBase64, string onErrorBase64, string onNullBase64) { RequireServer.Check().Supports(Feature.ConvertBinDataToFromNumeric); @@ -145,10 +145,10 @@ public void MongoDBFunctions_ConvertToBinDataFromIntWithOnErrorAndOnNull_should_ } [Theory] - [InlineData(1, Mql.ByteOrder.BigEndian, null, "FormatException")] - [InlineData(4, Mql.ByteOrder.LittleEndian,"ogIAAA==", null)] - [InlineData(6, Mql.ByteOrder.BigEndian, "AAAAKg==", null )] - public void MongoDBFunctions_ConvertToLongDataFromInt_should_work(int id, Mql.ByteOrder byteOrder, string expectedBase64, string expectedException) + [InlineData(1, ByteOrder.BigEndian, null, "FormatException")] + [InlineData(4, ByteOrder.LittleEndian,"ogIAAA==", null)] + [InlineData(6, ByteOrder.BigEndian, "AAAAKg==", null )] + public void MongoDBFunctions_ConvertToLongDataFromInt_should_work(int id, ByteOrder byteOrder, string expectedBase64, string expectedException) { RequireServer.Check().Supports(Feature.ConvertBinDataToFromNumeric); @@ -169,7 +169,7 @@ public void MongoDBFunctions_ConvertToLongDataFromInt_should_work(int id, Mql.By { //$convert to bindata returns always 8 bytes when from long var expectedBytes = new byte[8]; - Array.Copy(Convert.FromBase64String(expectedBase64), 0, expectedBytes, byteOrder is Mql.ByteOrder.LittleEndian ? 0 : 4, 4); + Array.Copy(Convert.FromBase64String(expectedBase64), 0, expectedBytes, byteOrder is ByteOrder.LittleEndian ? 0 : 4, 4); expectedResult = new BsonBinaryData(expectedBytes); } @@ -177,11 +177,11 @@ public void MongoDBFunctions_ConvertToLongDataFromInt_should_work(int id, Mql.By } [Theory] - [InlineData(0, Mql.ByteOrder.LittleEndian, "AAAAAAAABMA=", "Ag==", "AAAAAAAABMA=")] - [InlineData(10, Mql.ByteOrder.LittleEndian, "Ag==", "Ag==", "AAAAAAAABMA=")] - [InlineData(0, Mql.ByteOrder.BigEndian, "AAAAAAAABMA=", "Ag==", "AAAAAAAABMA=")] - [InlineData(10, Mql.ByteOrder.BigEndian, "Ag==", "Ag==", "AAAAAAAABMA=")] - public void MongoDBFunctions_ConvertToLongDataFromIntWithOnErrorAndOnNull_should_work(int id, Mql.ByteOrder byteOrder, string expectedBase64, string onErrorBase64, string onNullBase64) + [InlineData(0, ByteOrder.LittleEndian, "AAAAAAAABMA=", "Ag==", "AAAAAAAABMA=")] + [InlineData(10, ByteOrder.LittleEndian, "Ag==", "Ag==", "AAAAAAAABMA=")] + [InlineData(0, ByteOrder.BigEndian, "AAAAAAAABMA=", "Ag==", "AAAAAAAABMA=")] + [InlineData(10, ByteOrder.BigEndian, "Ag==", "Ag==", "AAAAAAAABMA=")] + public void MongoDBFunctions_ConvertToLongDataFromIntWithOnErrorAndOnNull_should_work(int id, ByteOrder byteOrder, string expectedBase64, string onErrorBase64, string onNullBase64) { RequireServer.Check().Supports(Feature.ConvertBinDataToFromNumeric); @@ -261,10 +261,10 @@ public void MongoDBFunctions_ConvertToBinDataFromStringWithOnErrorAndOnNull_shou // To Double [Theory] - [InlineData(2, Mql.ByteOrder.BigEndian, null, "MongoCommandException")] - [InlineData(3, Mql.ByteOrder.LittleEndian, -0.5, null)] - [InlineData(5, Mql.ByteOrder.BigEndian, -2.5, null)] - public void MongoDBFunctions_ConvertToDoubleFromBinData_should_work(int id, Mql.ByteOrder byteOrder, double? expectedResult, string expectedException) + [InlineData(2, ByteOrder.BigEndian, null, "MongoCommandException")] + [InlineData(3, ByteOrder.LittleEndian, -0.5, null)] + [InlineData(5, ByteOrder.BigEndian, -2.5, null)] + public void MongoDBFunctions_ConvertToDoubleFromBinData_should_work(int id, ByteOrder byteOrder, double? expectedResult, string expectedException) { RequireServer.Check().Supports(Feature.ConvertBinDataToFromNumeric); @@ -284,13 +284,13 @@ public void MongoDBFunctions_ConvertToDoubleFromBinData_should_work(int id, Mql. } [Theory] - [InlineData(2, Mql.ByteOrder.LittleEndian, 15.2, 15.2, 22.3)] - [InlineData(0, Mql.ByteOrder.LittleEndian, 22.3, 15.2, 22.3)] - [InlineData(2, Mql.ByteOrder.BigEndian, 15.2, 15.2, 22.3)] - [InlineData(0, Mql.ByteOrder.BigEndian, 22.3, 15.2, 22.3)] - [InlineData(2, Mql.ByteOrder.LittleEndian, null, null, 22.3)] - [InlineData(0, Mql.ByteOrder.LittleEndian, null, 15.2, null)] - public void MongoDBFunctions_ConvertToDoubleFromBinDataWithOnErrorAndOnNull_should_work(int id, Mql.ByteOrder byteOrder, double? expectedResult, double? onError, double? onNull) + [InlineData(2, ByteOrder.LittleEndian, 15.2, 15.2, 22.3)] + [InlineData(0, ByteOrder.LittleEndian, 22.3, 15.2, 22.3)] + [InlineData(2, ByteOrder.BigEndian, 15.2, 15.2, 22.3)] + [InlineData(0, ByteOrder.BigEndian, 22.3, 15.2, 22.3)] + [InlineData(2, ByteOrder.LittleEndian, null, null, 22.3)] + [InlineData(0, ByteOrder.LittleEndian, null, 15.2, null)] + public void MongoDBFunctions_ConvertToDoubleFromBinDataWithOnErrorAndOnNull_should_work(int id, ByteOrder byteOrder, double? expectedResult, double? onError, double? onNull) { RequireServer.Check().Supports(Feature.ConvertBinDataToFromNumeric); @@ -314,10 +314,10 @@ public void MongoDBFunctions_ConvertToDoubleFromBinDataWithOnErrorAndOnNull_shou // To Int [Theory] - [InlineData(2, Mql.ByteOrder.LittleEndian, null, "MongoCommandException")] - [InlineData(4, Mql.ByteOrder.LittleEndian, 674, null)] - [InlineData(6, Mql.ByteOrder.BigEndian, 42, null)] - public void MongoDBFunctions_ConvertToIntFromBinData_should_work(int id, Mql.ByteOrder byteOrder, int? expectedResult, string expectedException) + [InlineData(2, ByteOrder.LittleEndian, null, "MongoCommandException")] + [InlineData(4, ByteOrder.LittleEndian, 674, null)] + [InlineData(6, ByteOrder.BigEndian, 42, null)] + public void MongoDBFunctions_ConvertToIntFromBinData_should_work(int id, ByteOrder byteOrder, int? expectedResult, string expectedException) { RequireServer.Check().Supports(Feature.ConvertBinDataToFromNumeric); @@ -337,13 +337,13 @@ public void MongoDBFunctions_ConvertToIntFromBinData_should_work(int id, Mql.Byt } [Theory] - [InlineData(2, Mql.ByteOrder.LittleEndian, 15, 15, 22)] - [InlineData(0, Mql.ByteOrder.LittleEndian, 22, 15, 22)] - [InlineData(2, Mql.ByteOrder.BigEndian, 15, 15, 22)] - [InlineData(0, Mql.ByteOrder.BigEndian, 22, 15, 22)] - [InlineData(2, Mql.ByteOrder.LittleEndian, null, null, 22)] - [InlineData(0, Mql.ByteOrder.LittleEndian, null, 15, null)] - public void MongoDBFunctions_ConvertToIntFromBinDataWithOnErrorAndOnNull_should_work(int id, Mql.ByteOrder byteOrder, int? expectedResult, int? onError, int? onNull) + [InlineData(2, ByteOrder.LittleEndian, 15, 15, 22)] + [InlineData(0, ByteOrder.LittleEndian, 22, 15, 22)] + [InlineData(2, ByteOrder.BigEndian, 15, 15, 22)] + [InlineData(0, ByteOrder.BigEndian, 22, 15, 22)] + [InlineData(2, ByteOrder.LittleEndian, null, null, 22)] + [InlineData(0, ByteOrder.LittleEndian, null, 15, null)] + public void MongoDBFunctions_ConvertToIntFromBinDataWithOnErrorAndOnNull_should_work(int id, ByteOrder byteOrder, int? expectedResult, int? onError, int? onNull) { RequireServer.Check().Supports(Feature.ConvertBinDataToFromNumeric); @@ -367,10 +367,10 @@ public void MongoDBFunctions_ConvertToIntFromBinDataWithOnErrorAndOnNull_should_ // To Long [Theory] - [InlineData(2, Mql.ByteOrder.LittleEndian, null, "MongoCommandException")] - [InlineData(4, Mql.ByteOrder.LittleEndian, (long)674, null)] - [InlineData(6, Mql.ByteOrder.BigEndian, (long)42, null)] - public void MongoDBFunctions_ConvertToLongFromBinData_should_work(int id, Mql.ByteOrder byteOrder, long? expectedResult, string expectedException) + [InlineData(2, ByteOrder.LittleEndian, null, "MongoCommandException")] + [InlineData(4, ByteOrder.LittleEndian, (long)674, null)] + [InlineData(6, ByteOrder.BigEndian, (long)42, null)] + public void MongoDBFunctions_ConvertToLongFromBinData_should_work(int id, ByteOrder byteOrder, long? expectedResult, string expectedException) { RequireServer.Check().Supports(Feature.ConvertBinDataToFromNumeric); @@ -390,13 +390,13 @@ public void MongoDBFunctions_ConvertToLongFromBinData_should_work(int id, Mql.By } [Theory] - [InlineData(2, Mql.ByteOrder.LittleEndian, (long)15, (long)15, (long)22)] - [InlineData(0, Mql.ByteOrder.LittleEndian, (long)22, (long)15, (long)22)] - [InlineData(2, Mql.ByteOrder.BigEndian, (long)15, (long)15, (long)22)] - [InlineData(0, Mql.ByteOrder.BigEndian, (long)22, (long)15, (long)22)] - [InlineData(2, Mql.ByteOrder.LittleEndian, null, null, (long)22)] - [InlineData(0, Mql.ByteOrder.LittleEndian, null, (long)15, null)] - public void MongoDBFunctions_ConvertToLongFromBinDataWithOnErrorAndOnNull_should_work(int id, Mql.ByteOrder byteOrder, long? expectedResult, long? onError, long? onNull) + [InlineData(2, ByteOrder.LittleEndian, (long)15, (long)15, (long)22)] + [InlineData(0, ByteOrder.LittleEndian, (long)22, (long)15, (long)22)] + [InlineData(2, ByteOrder.BigEndian, (long)15, (long)15, (long)22)] + [InlineData(0, ByteOrder.BigEndian, (long)22, (long)15, (long)22)] + [InlineData(2, ByteOrder.LittleEndian, null, null, (long)22)] + [InlineData(0, ByteOrder.LittleEndian, null, (long)15, null)] + public void MongoDBFunctions_ConvertToLongFromBinDataWithOnErrorAndOnNull_should_work(int id, ByteOrder byteOrder, long? expectedResult, long? onError, long? onNull) { RequireServer.Check().Supports(Feature.ConvertBinDataToFromNumeric); @@ -492,12 +492,12 @@ private void AssertOutcome(IMongoCollection collection, } } - private string ByteOrderToString(Mql.ByteOrder byteOrder) + private string ByteOrderToString(ByteOrder byteOrder) { var byteOrderString = byteOrder switch { - Mql.ByteOrder.BigEndian => "big", - Mql.ByteOrder.LittleEndian => "little", + ByteOrder.BigEndian => "big", + ByteOrder.LittleEndian => "little", _ => throw new ArgumentOutOfRangeException(nameof(byteOrder), byteOrder, null) }; From 37ab91e380486cdebe1651a1e52b2fc44feba279 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Tue, 25 Mar 2025 11:04:54 +0100 Subject: [PATCH 29/82] Changed order --- .../Linq3Implementation/Ast/Expressions/AstExpression.cs | 5 +++-- .../ConvertMethodToAggregationExpressionTranslator.cs | 2 +- .../Ast/Expressions/AstExpressionTests.cs | 4 ++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstExpression.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstExpression.cs index c7d9973ec1a..7126a165873 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstExpression.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstExpression.cs @@ -255,8 +255,9 @@ public static AstExpression Constant(BsonValue value) return new AstConstantExpression(value); } - public static AstExpression Convert(AstExpression input, AstExpression to, AstExpression onError = null, AstExpression onNull = null, - BsonBinarySubType? subType = null, string format = null, ByteOrder? byteOrder = null) + public static AstExpression Convert(AstExpression input, AstExpression to, + BsonBinarySubType? subType = null, ByteOrder? byteOrder = null, string format = null, + AstExpression onError = null, AstExpression onNull = null) { Ensure.IsNotNull(input, nameof(input)); Ensure.IsNotNull(to, nameof(to)); diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs index 4e6422a20cd..58a95152f6a 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs @@ -85,7 +85,7 @@ public static TranslatedExpression Translate(TranslationContext context, MethodC } } - var ast = AstExpression.Convert(fieldAst, mapping.Type.Render(), onError: onErrorAst, onNull: onNullAst, subType: subType, format: format, byteOrder: byteOrder); + var ast = AstExpression.Convert(fieldAst, mapping.Type.Render(), subType: subType, byteOrder: byteOrder, format: format, onError: onErrorAst, onNull: onNullAst); return new TranslatedExpression(expression, ast, mapping.Serializer); } } diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Ast/Expressions/AstExpressionTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Ast/Expressions/AstExpressionTests.cs index cfb89cb48b0..0f313e27f50 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Ast/Expressions/AstExpressionTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Ast/Expressions/AstExpressionTests.cs @@ -97,7 +97,7 @@ public void Convert_with_on_error_should_return_long_form(string toValue) var to = AstExpression.Constant(toValue); var onError = AstExpression.Constant(BsonNull.Value); - var result = AstExpression.Convert(input, to, onError, onNull: null); + var result = AstExpression.Convert(input, to, onError: onError, onNull: null); var convertExpression = result.Should().BeOfType().Subject; convertExpression.Input.Should().BeSameAs(input); @@ -121,7 +121,7 @@ public void Convert_with_on_null_should_return_long_form(string toValue) var to = AstExpression.Constant(toValue); var onNull = AstExpression.Constant(BsonNull.Value); - var result = AstExpression.Convert(input, to, onError: null, onNull); + var result = AstExpression.Convert(input, to, onError: null, onNull: onNull); var convertExpression = result.Should().BeOfType().Subject; convertExpression.Input.Should().BeSameAs(input); From 54395c1a2bcc00038c24ec86ac390fe5fcedba5a Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Tue, 25 Mar 2025 11:27:58 +0100 Subject: [PATCH 30/82] Improved testing --- ...dToAggregationExpressionTranslatorTests.cs | 39 +++++-------------- 1 file changed, 10 insertions(+), 29 deletions(-) diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs index dbf63e7a79c..699ba7fabbd 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs @@ -504,43 +504,24 @@ private string ByteOrderToString(ByteOrder byteOrder) return $"byteOrder: '{byteOrderString}'"; } - public sealed class ClassFixture : MongoCollectionFixture + public sealed class ClassFixture : MongoCollectionFixture { - private bool _initialized; - - protected override IEnumerable InitialData { get; } = + protected override IEnumerable InitialData => [ - new TestClass {Id = 0 }, - new TestClass {Id = 1, BinaryProperty = new BsonBinaryData([0, 1, 2])}, - new TestClass {Id = 2, BinaryProperty = new BsonBinaryData(Guid.Parse("867dee52-c331-484e-92d1-c56479b8e67e"), GuidRepresentation.Standard), StringProperty = "867dee52-c331-484e-92d1-c56479b8e67e"}, - new TestClass {Id = 3, BinaryProperty = new BsonBinaryData(Convert.FromBase64String("AAAAAAAA4L8=")), DoubleProperty = -0.5}, //LittleEndian - new TestClass {Id = 4, BinaryProperty = new BsonBinaryData(Convert.FromBase64String("ogIAAA==")), IntProperty = 674, LongProperty = 674}, //LittleEndian - new TestClass {Id = 5, BinaryProperty = new BsonBinaryData(Convert.FromBase64String("wAQAAAAAAAA=")), DoubleProperty = -2.5}, //BigEndian - new TestClass {Id = 6, BinaryProperty = new BsonBinaryData(Convert.FromBase64String("AAAAKg==")), IntProperty = 42, LongProperty = 42}, //BigEndian + BsonDocument.Parse("{ _id : 0 }"), + BsonDocument.Parse("{ _id : 1, BinaryProperty : BinData(0, 'ogIAAA==') }"), + BsonDocument.Parse("{ _id : 2, BinaryProperty : BinData(4, 'hn3uUsMxSE6S0cVkebjmfg=='), StringProperty: '867dee52-c331-484e-92d1-c56479b8e67e' }"), + BsonDocument.Parse("{ _id : 3, BinaryProperty : BinData(0, 'AAAAAAAA4L8='), DoubleProperty: -0.5 }"), //LittleEndian + BsonDocument.Parse("{ _id : 4, BinaryProperty : BinData(0, 'ogIAAA=='), IntProperty: 674, LongProperty: NumberLong('674') }"), //LittleEndian + BsonDocument.Parse("{ _id : 5, BinaryProperty : BinData(0, 'wAQAAAAAAAA='), DoubleProperty: -2.5 }"), //BigEndian + BsonDocument.Parse("{ _id : 6, BinaryProperty : BinData(0, 'AAAAKg=='), IntProperty: 42, LongProperty: NumberLong('42') }"), //BigEndian + BsonDocument.Parse("{ _id: 10, DoubleProperty: NumberDecimal('-32768'), IntProperty: NumberDecimal('-32768'), LongProperty: NumberDecimal('-32768'), StringProperty: NumberDecimal('-233') }") ]; - - protected override void InitializeTestCase() - { - base.InitializeTestCase(); - - if (_initialized) - { - return; - } - - _initialized = true; - - //It is not possible to create a $convert that causes an error when converting int/long/double to binData using the typed API. Converting 'decimal' to binData is not allowed. - var parsed = BsonDocument.Parse("{ _id: 10, DoubleProperty: NumberDecimal('-32768'), IntProperty: NumberDecimal('-32768'), LongProperty: NumberDecimal('-32768'), StringProperty: NumberDecimal('-233') }"); - var untypedCollection = Database.GetCollection(GetCollectionName()); - untypedCollection.InsertOne(parsed); - } } public class TestClass { public int Id { get; set; } - [BsonIgnoreIfDefault] public BsonBinaryData BinaryProperty { get; set; } public double? DoubleProperty { get; set; } public int? IntProperty { get; set; } From 5e5701e633d5de1630e325c3ef737133513218b1 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Tue, 25 Mar 2025 11:58:56 +0100 Subject: [PATCH 31/82] Rename --- .../Reflection/MqlMethod.cs | 104 +++++++++--------- ...essionToAggregationExpressionTranslator.cs | 9 +- ...MethodToAggregationExpressionTranslator.cs | 34 +++--- ...MethodToAggregationExpressionTranslator.cs | 5 + src/MongoDB.Driver/Mql.cs | 32 +++--- ...dToAggregationExpressionTranslatorTests.cs | 32 +++--- 6 files changed, 114 insertions(+), 102 deletions(-) diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/MqlMethod.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/MqlMethod.cs index 5178b178d4b..cd3c2a9aa59 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/MqlMethod.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/MqlMethod.cs @@ -34,22 +34,22 @@ internal static class MqlMethod private static readonly MethodInfo __isMissing; private static readonly MethodInfo __isNullOrMissing; - private static readonly MethodInfo __convertToBinDataFromDouble; - private static readonly MethodInfo __convertToBinDataFromDoubleWithOnErrorAndOnNull; - private static readonly MethodInfo __convertToBinDataFromInt; - private static readonly MethodInfo __convertToBinDataFromIntWithOnErrorAndOnNull; - private static readonly MethodInfo __convertToBinDataFromLong; - private static readonly MethodInfo __convertToBinDataFromLongWithOnErrorAndOnNull; - private static readonly MethodInfo __convertToBinDataFromString; - private static readonly MethodInfo __convertToBinDataFromStringWithOnErrorAndOnNull; - private static readonly MethodInfo __convertToNullableDoubleFromBinData; - private static readonly MethodInfo __convertToNullableDoubleFromBinDataWithOnErrorAndOnNull; - private static readonly MethodInfo __convertToNullableIntFromBinData; - private static readonly MethodInfo __convertToNullableIntFromBinDataWithOnErrorAndOnNull; - private static readonly MethodInfo __convertToNullableLongFromBinData; - private static readonly MethodInfo __convertToNullableLongFromBinDataWithOnErrorAndOnNull; - private static readonly MethodInfo __convertToStringFromBinData; - private static readonly MethodInfo __convertToStringFromBinDataWithOnErrorAndOnNull; + private static readonly MethodInfo __toBinDataFromDouble; + private static readonly MethodInfo __toBinDataFromDoubleWithOnErrorAndOnNull; + private static readonly MethodInfo __toBinDataFromInt; + private static readonly MethodInfo __toBinDataFromIntWithOnErrorAndOnNull; + private static readonly MethodInfo __toBinDataFromLong; + private static readonly MethodInfo __toBinDataFromLongWithOnErrorAndOnNull; + private static readonly MethodInfo __toBinDataFromString; + private static readonly MethodInfo __toBinDataFromStringWithOnErrorAndOnNull; + private static readonly MethodInfo __toNullableDoubleFromBinData; + private static readonly MethodInfo __toNullableDoubleFromBinDataWithOnErrorAndOnNull; + private static readonly MethodInfo __toNullableIntFromBinData; + private static readonly MethodInfo __toNullableIntFromBinDataWithOnErrorAndOnNull; + private static readonly MethodInfo __toNullableLongFromBinData; + private static readonly MethodInfo __toNullableLongFromBinDataWithOnErrorAndOnNull; + private static readonly MethodInfo __toStringFromBinData; + private static readonly MethodInfo __toStringFromBinDataWithOnErrorAndOnNull; // static constructor static MqlMethod() @@ -66,30 +66,30 @@ static MqlMethod() __isNullOrMissing = ReflectionInfo.Method((object field) => Mql.IsNullOrMissing(field)); // Convert methods - __convertToBinDataFromDouble = ReflectionInfo.Method((double? field, BsonBinarySubType subType, ByteOrder byteOrder) => Mql.ConvertToBinData(field, subType, byteOrder)); - __convertToBinDataFromDoubleWithOnErrorAndOnNull = ReflectionInfo.Method((double? field, BsonBinarySubType subType, ByteOrder byteOrder, BsonBinaryData onError, BsonBinaryData onNull) - => Mql.ConvertToBinData(field, subType, byteOrder, onError, onNull)); - __convertToBinDataFromInt = ReflectionInfo.Method((int? field, BsonBinarySubType subType, ByteOrder byteOrder) => Mql.ConvertToBinData(field, subType, byteOrder)); - __convertToBinDataFromIntWithOnErrorAndOnNull = ReflectionInfo.Method((int? field, BsonBinarySubType subType, ByteOrder byteOrder, BsonBinaryData onError, BsonBinaryData onNull) - => Mql.ConvertToBinData(field, subType, byteOrder, onError, onNull)); - __convertToBinDataFromLong = ReflectionInfo.Method((long? field, BsonBinarySubType subType, ByteOrder byteOrder) => Mql.ConvertToBinData(field, subType, byteOrder)); - __convertToBinDataFromLongWithOnErrorAndOnNull = ReflectionInfo.Method((long? field, BsonBinarySubType subType, ByteOrder byteOrder, BsonBinaryData onError, BsonBinaryData onNull) - => Mql.ConvertToBinData(field, subType, byteOrder, onError, onNull)); - __convertToBinDataFromString = ReflectionInfo.Method((string field, BsonBinarySubType subType, string format) => Mql.ConvertToBinData(field, subType, format)); - __convertToBinDataFromStringWithOnErrorAndOnNull = ReflectionInfo.Method((string field, BsonBinarySubType subType, string format, BsonBinaryData onError, BsonBinaryData onNull) - => Mql.ConvertToBinData(field, subType, format, onError, onNull)); + __toBinDataFromDouble = ReflectionInfo.Method((double? field, BsonBinarySubType subType, ByteOrder byteOrder) => Mql.ToBinData(field, subType, byteOrder)); + __toBinDataFromDoubleWithOnErrorAndOnNull = ReflectionInfo.Method((double? field, BsonBinarySubType subType, ByteOrder byteOrder, BsonBinaryData onError, BsonBinaryData onNull) + => Mql.ToBinData(field, subType, byteOrder, onError, onNull)); + __toBinDataFromInt = ReflectionInfo.Method((int? field, BsonBinarySubType subType, ByteOrder byteOrder) => Mql.ToBinData(field, subType, byteOrder)); + __toBinDataFromIntWithOnErrorAndOnNull = ReflectionInfo.Method((int? field, BsonBinarySubType subType, ByteOrder byteOrder, BsonBinaryData onError, BsonBinaryData onNull) + => Mql.ToBinData(field, subType, byteOrder, onError, onNull)); + __toBinDataFromLong = ReflectionInfo.Method((long? field, BsonBinarySubType subType, ByteOrder byteOrder) => Mql.ToBinData(field, subType, byteOrder)); + __toBinDataFromLongWithOnErrorAndOnNull = ReflectionInfo.Method((long? field, BsonBinarySubType subType, ByteOrder byteOrder, BsonBinaryData onError, BsonBinaryData onNull) + => Mql.ToBinData(field, subType, byteOrder, onError, onNull)); + __toBinDataFromString = ReflectionInfo.Method((string field, BsonBinarySubType subType, string format) => Mql.ToBinData(field, subType, format)); + __toBinDataFromStringWithOnErrorAndOnNull = ReflectionInfo.Method((string field, BsonBinarySubType subType, string format, BsonBinaryData onError, BsonBinaryData onNull) + => Mql.ToBinData(field, subType, format, onError, onNull)); - __convertToNullableDoubleFromBinData = ReflectionInfo.Method((BsonBinaryData field, ByteOrder byteOrder) => Mql.ConvertToNullableDouble(field, byteOrder)); - __convertToNullableDoubleFromBinDataWithOnErrorAndOnNull = ReflectionInfo.Method((BsonBinaryData field, ByteOrder byteOrder, double? onError, double? onNull) => Mql.ConvertToNullableDouble(field, byteOrder, onError, onNull)); + __toNullableDoubleFromBinData = ReflectionInfo.Method((BsonBinaryData field, ByteOrder byteOrder) => Mql.ToNullableDouble(field, byteOrder)); + __toNullableDoubleFromBinDataWithOnErrorAndOnNull = ReflectionInfo.Method((BsonBinaryData field, ByteOrder byteOrder, double? onError, double? onNull) => Mql.ToNullableDouble(field, byteOrder, onError, onNull)); - __convertToNullableIntFromBinData = ReflectionInfo.Method((BsonBinaryData field, ByteOrder byteOrder) => Mql.ConvertToNullableInt(field, byteOrder)); - __convertToNullableIntFromBinDataWithOnErrorAndOnNull = ReflectionInfo.Method((BsonBinaryData field, ByteOrder byteOrder, int? onError, int? onNull) => Mql.ConvertToNullableInt(field, byteOrder, onError, onNull)); + __toNullableIntFromBinData = ReflectionInfo.Method((BsonBinaryData field, ByteOrder byteOrder) => Mql.ToNullableInt(field, byteOrder)); + __toNullableIntFromBinDataWithOnErrorAndOnNull = ReflectionInfo.Method((BsonBinaryData field, ByteOrder byteOrder, int? onError, int? onNull) => Mql.ToNullableInt(field, byteOrder, onError, onNull)); - __convertToNullableLongFromBinData = ReflectionInfo.Method((BsonBinaryData field, ByteOrder byteOrder) => Mql.ConvertToNullableLong(field, byteOrder)); - __convertToNullableLongFromBinDataWithOnErrorAndOnNull = ReflectionInfo.Method((BsonBinaryData field, ByteOrder byteOrder, long? onError, long? onNull) => Mql.ConvertToNullableLong(field, byteOrder, onError, onNull)); + __toNullableLongFromBinData = ReflectionInfo.Method((BsonBinaryData field, ByteOrder byteOrder) => Mql.ToNullableLong(field, byteOrder)); + __toNullableLongFromBinDataWithOnErrorAndOnNull = ReflectionInfo.Method((BsonBinaryData field, ByteOrder byteOrder, long? onError, long? onNull) => Mql.ToNullableLong(field, byteOrder, onError, onNull)); - __convertToStringFromBinData = ReflectionInfo.Method((BsonBinaryData field, string format) => Mql.ConvertToString(field, format)); - __convertToStringFromBinDataWithOnErrorAndOnNull = ReflectionInfo.Method((BsonBinaryData field, string format, string onError, string onNull) => Mql.ConvertToString(field, format, onError, onNull)); + __toStringFromBinData = ReflectionInfo.Method((BsonBinaryData field, string format) => Mql.ToString(field, format)); + __toStringFromBinDataWithOnErrorAndOnNull = ReflectionInfo.Method((BsonBinaryData field, string format, string onError, string onNull) => Mql.ToString(field, format, onError, onNull)); } // public properties @@ -103,21 +103,21 @@ static MqlMethod() public static MethodInfo Field => __field; public static MethodInfo IsMissing => __isMissing; public static MethodInfo IsNullOrMissing => __isNullOrMissing; - public static MethodInfo ConvertToBinDataFromDouble => __convertToBinDataFromDouble; - public static MethodInfo ConvertToBinDataFromDoubleWithOnErrorAndOnNull => __convertToBinDataFromDoubleWithOnErrorAndOnNull; - public static MethodInfo ConvertToBinDataFromInt => __convertToBinDataFromInt; - public static MethodInfo ConvertToBinDataFromIntWithOnErrorAndOnNull => __convertToBinDataFromIntWithOnErrorAndOnNull; - public static MethodInfo ConvertToBinDataFromLong => __convertToBinDataFromLong; - public static MethodInfo ConvertToBinDataFromLongWithOnErrorAndOnNull => __convertToBinDataFromLongWithOnErrorAndOnNull; - public static MethodInfo ConvertToBinDataFromString => __convertToBinDataFromString; - public static MethodInfo ConvertToBinDataFromStringWithOnErrorAndOnNull => __convertToBinDataFromStringWithOnErrorAndOnNull; - public static MethodInfo ConvertToNullableDoubleFromBinData => __convertToNullableDoubleFromBinData; - public static MethodInfo ConvertToNullableDoubleFromBinDataWithOnErrorAndOnNull => __convertToNullableDoubleFromBinDataWithOnErrorAndOnNull; - public static MethodInfo ConvertToNullableIntFromBinData => __convertToNullableIntFromBinData; - public static MethodInfo ConvertToNullableIntFromBinDataWithOnErrorAndOnNull => __convertToNullableIntFromBinDataWithOnErrorAndOnNull; - public static MethodInfo ConvertToNullableLongFromBinData => __convertToNullableLongFromBinData; - public static MethodInfo ConvertToNullableLongFromBinDataWithOnErrorAndOnNull => __convertToNullableLongFromBinDataWithOnErrorAndOnNull; - public static MethodInfo ConvertToStringFromBinData => __convertToStringFromBinData; - public static MethodInfo ConvertToStringFromBinDataWithOnErrorAndOnNull => __convertToStringFromBinDataWithOnErrorAndOnNull; + public static MethodInfo ToBinDataFromDouble => __toBinDataFromDouble; + public static MethodInfo ToBinDataFromDoubleWithOnErrorAndOnNull => __toBinDataFromDoubleWithOnErrorAndOnNull; + public static MethodInfo ToBinDataFromInt => __toBinDataFromInt; + public static MethodInfo ToBinDataFromIntWithOnErrorAndOnNull => __toBinDataFromIntWithOnErrorAndOnNull; + public static MethodInfo ToBinDataFromLong => __toBinDataFromLong; + public static MethodInfo ToBinDataFromLongWithOnErrorAndOnNull => __toBinDataFromLongWithOnErrorAndOnNull; + public static MethodInfo ToBinDataFromString => __toBinDataFromString; + public static MethodInfo ToBinDataFromStringWithOnErrorAndOnNull => __toBinDataFromStringWithOnErrorAndOnNull; + public static MethodInfo ToNullableDoubleFromBinData => __toNullableDoubleFromBinData; + public static MethodInfo ToNullableDoubleFromBinDataWithOnErrorAndOnNull => __toNullableDoubleFromBinDataWithOnErrorAndOnNull; + public static MethodInfo ToNullableIntFromBinData => __toNullableIntFromBinData; + public static MethodInfo ToNullableIntFromBinDataWithOnErrorAndOnNull => __toNullableIntFromBinDataWithOnErrorAndOnNull; + public static MethodInfo ToNullableLongFromBinData => __toNullableLongFromBinData; + public static MethodInfo ToNullableLongFromBinDataWithOnErrorAndOnNull => __toNullableLongFromBinDataWithOnErrorAndOnNull; + public static MethodInfo ToStringFromBinData => __toStringFromBinData; + public static MethodInfo ToStringFromBinDataWithOnErrorAndOnNull => __toStringFromBinDataWithOnErrorAndOnNull; } } diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodCallExpressionToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodCallExpressionToAggregationExpressionTranslator.cs index c13d6956842..ca1723fe72e 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodCallExpressionToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodCallExpressionToAggregationExpressionTranslator.cs @@ -138,11 +138,10 @@ public static TranslatedExpression Translate(TranslationContext context, MethodC case "LongCount": return CountMethodToAggregationExpressionTranslator.Translate(context, expression); - case "ConvertToBinData": - case "ConvertToNullableDouble": - case "ConvertToNullableInt": - case "ConvertToNullableLong": - case "ConvertToString": + case "ToBinData": + case "ToNullableDouble": + case "ToNullableInt": + case "ToNullableLong": return ConvertMethodToAggregationExpressionTranslator.Translate(context, expression); case "ElementAt": diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs index 58a95152f6a..d980b04cd58 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs @@ -16,21 +16,30 @@ internal class ConvertMethodToAggregationExpressionTranslator { private static readonly List<(MethodInfo[] Methods, IBsonSerializer Serializer, BsonType Type, int? FormatIndex, int? SubTypeIndex, int? ByteOrderIndex, int? OnErrorIndex, int? OnNullIndex)> _methodMappings = [ - ([MqlMethod.ConvertToBinDataFromString], BsonBinaryDataSerializer.Instance, BsonType.Binary, 2, 1, null, null, null), - ([MqlMethod.ConvertToBinDataFromInt, MqlMethod.ConvertToBinDataFromLong, MqlMethod.ConvertToBinDataFromDouble], + ([MqlMethod.ToBinDataFromString], BsonBinaryDataSerializer.Instance, BsonType.Binary, 2, 1, null, null, null), + ([MqlMethod.ToBinDataFromInt, MqlMethod.ToBinDataFromLong, MqlMethod.ToBinDataFromDouble], BsonBinaryDataSerializer.Instance, BsonType.Binary, null, 1, 2, null, null), - ([MqlMethod.ConvertToBinDataFromStringWithOnErrorAndOnNull], BsonBinaryDataSerializer.Instance, BsonType.Binary, 2, 1, null, 3, 4), - ([MqlMethod.ConvertToBinDataFromIntWithOnErrorAndOnNull, MqlMethod.ConvertToBinDataFromLongWithOnErrorAndOnNull, MqlMethod.ConvertToBinDataFromDoubleWithOnErrorAndOnNull], + ([MqlMethod.ToBinDataFromStringWithOnErrorAndOnNull], BsonBinaryDataSerializer.Instance, BsonType.Binary, 2, 1, null, 3, 4), + ([MqlMethod.ToBinDataFromIntWithOnErrorAndOnNull, MqlMethod.ToBinDataFromLongWithOnErrorAndOnNull, MqlMethod.ToBinDataFromDoubleWithOnErrorAndOnNull], BsonBinaryDataSerializer.Instance, BsonType.Binary, null, 1, 2, 3, 4), - ([MqlMethod.ConvertToStringFromBinData], StringSerializer.Instance, BsonType.String, 1, null, null, null, null), - ([MqlMethod.ConvertToStringFromBinDataWithOnErrorAndOnNull], StringSerializer.Instance, BsonType.String, 1, null, null, 2, 3), - ([MqlMethod.ConvertToNullableIntFromBinData], new NullableSerializer(Int32Serializer.Instance), BsonType.Int32, null, null, 1, null, null), - ([MqlMethod.ConvertToNullableIntFromBinDataWithOnErrorAndOnNull], new NullableSerializer(Int32Serializer.Instance), BsonType.Int32, null, null, 1, 2, 3), - ([MqlMethod.ConvertToNullableLongFromBinData], new NullableSerializer(Int64Serializer.Instance), BsonType.Int64, null, null, 1, null, null), - ([MqlMethod.ConvertToNullableLongFromBinDataWithOnErrorAndOnNull], new NullableSerializer(Int64Serializer.Instance), BsonType.Int64, null, null, 1, 2, 3), - ([MqlMethod.ConvertToNullableDoubleFromBinData], new NullableSerializer(DoubleSerializer.Instance), BsonType.Double, null, null, 1, null, null), - ([MqlMethod.ConvertToNullableDoubleFromBinDataWithOnErrorAndOnNull], new NullableSerializer(DoubleSerializer.Instance), BsonType.Double, null, null, 1, 2, 3) + ([MqlMethod.ToStringFromBinData], StringSerializer.Instance, BsonType.String, 1, null, null, null, null), + ([MqlMethod.ToStringFromBinDataWithOnErrorAndOnNull], StringSerializer.Instance, BsonType.String, 1, null, null, 2, 3), + ([MqlMethod.ToNullableIntFromBinData], new NullableSerializer(Int32Serializer.Instance), BsonType.Int32, null, null, 1, null, null), + ([MqlMethod.ToNullableIntFromBinDataWithOnErrorAndOnNull], new NullableSerializer(Int32Serializer.Instance), BsonType.Int32, null, null, 1, 2, 3), + ([MqlMethod.ToNullableLongFromBinData], new NullableSerializer(Int64Serializer.Instance), BsonType.Int64, null, null, 1, null, null), + ([MqlMethod.ToNullableLongFromBinDataWithOnErrorAndOnNull], new NullableSerializer(Int64Serializer.Instance), BsonType.Int64, null, null, 1, 2, 3), + ([MqlMethod.ToNullableDoubleFromBinData], new NullableSerializer(DoubleSerializer.Instance), BsonType.Double, null, null, 1, null, null), + ([MqlMethod.ToNullableDoubleFromBinDataWithOnErrorAndOnNull], new NullableSerializer(DoubleSerializer.Instance), BsonType.Double, null, null, 1, 2, 3) ]; + + private static readonly List ToStringMethods = + [MqlMethod.ToStringFromBinData, MqlMethod.ToStringFromBinDataWithOnErrorAndOnNull]; + + public static bool IsConvertToStringMethod(MethodInfo method) + { + return ToStringMethods.Contains(method); + } + public static TranslatedExpression Translate(TranslationContext context, MethodCallExpression expression) { var method = expression.Method; @@ -45,7 +54,6 @@ public static TranslatedExpression Translate(TranslationContext context, MethodC string format = null; var fieldAst = ExpressionToAggregationExpressionTranslator.Translate(context, arguments[0]).Ast; - var subTypeAst = mapping.SubTypeIndex.HasValue ? ExpressionToAggregationExpressionTranslator.Translate(context, arguments[mapping.SubTypeIndex.Value]).Ast : null; var onErrorAst = mapping.OnErrorIndex.HasValue ? ExpressionToAggregationExpressionTranslator.Translate(context, arguments[mapping.OnErrorIndex.Value]).Ast : null; var onNullAst = mapping.OnNullIndex.HasValue ? ExpressionToAggregationExpressionTranslator.Translate(context, arguments[mapping.OnNullIndex.Value]).Ast : null; diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ToStringMethodToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ToStringMethodToAggregationExpressionTranslator.cs index 79a62e7f055..bf54f764a3e 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ToStringMethodToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ToStringMethodToAggregationExpressionTranslator.cs @@ -58,6 +58,11 @@ public static TranslatedExpression Translate(TranslationContext context, MethodC return TranslateDateTimeToStringMethod(context, expression, method, arguments); } + if (ConvertMethodToAggregationExpressionTranslator.IsConvertToStringMethod(method)) + { + return ConvertMethodToAggregationExpressionTranslator.Translate(context, expression); + } + throw new ExpressionNotSupportedException(expression); } diff --git a/src/MongoDB.Driver/Mql.cs b/src/MongoDB.Driver/Mql.cs index 1250b629bcc..c5f87b05190 100644 --- a/src/MongoDB.Driver/Mql.cs +++ b/src/MongoDB.Driver/Mql.cs @@ -56,7 +56,7 @@ public static TValue Constant(TValue value, BsonType representaion) /// The BsonBinaryData subtype of the result value. /// The format string. /// The converted value. - public static BsonBinaryData ConvertToBinData(string value, BsonBinarySubType subtype, string format) + public static BsonBinaryData ToBinData(string value, BsonBinarySubType subtype, string format) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } @@ -70,7 +70,7 @@ public static BsonBinaryData ConvertToBinData(string value, BsonBinarySubType su /// The onError value. /// The onNull value. /// The converted value. - public static BsonBinaryData ConvertToBinData(string value, BsonBinarySubType subtype, string format, + public static BsonBinaryData ToBinData(string value, BsonBinarySubType subtype, string format, BsonBinaryData onError, BsonBinaryData onNull) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); @@ -83,7 +83,7 @@ public static BsonBinaryData ConvertToBinData(string value, BsonBinarySubType su /// The BsonBinaryData subtype of the result value. /// The byte order of BsonBinaryData. /// The converted value. - public static BsonBinaryData ConvertToBinData(int? value, BsonBinarySubType subtype, ByteOrder byteOrder) + public static BsonBinaryData ToBinData(int? value, BsonBinarySubType subtype, ByteOrder byteOrder) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } @@ -98,7 +98,7 @@ public static BsonBinaryData ConvertToBinData(int? value, BsonBinarySubType subt /// The onNull value. /// The converted value. /// - public static BsonBinaryData ConvertToBinData(int? value, BsonBinarySubType subtype, ByteOrder byteOrder, + public static BsonBinaryData ToBinData(int? value, BsonBinarySubType subtype, ByteOrder byteOrder, BsonBinaryData onError, BsonBinaryData onNull) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); @@ -111,7 +111,7 @@ public static BsonBinaryData ConvertToBinData(int? value, BsonBinarySubType subt /// The BsonBinaryData subtype of the result value. /// The byte ordering of BsonBinaryData. /// The converted value. - public static BsonBinaryData ConvertToBinData(long? value, BsonBinarySubType subtype, ByteOrder byteOrder) + public static BsonBinaryData ToBinData(long? value, BsonBinarySubType subtype, ByteOrder byteOrder) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } @@ -126,7 +126,7 @@ public static BsonBinaryData ConvertToBinData(long? value, BsonBinarySubType sub /// The onNull value. /// The converted value. /// - public static BsonBinaryData ConvertToBinData(long? value, BsonBinarySubType subtype, ByteOrder byteOrder, + public static BsonBinaryData ToBinData(long? value, BsonBinarySubType subtype, ByteOrder byteOrder, BsonBinaryData onError, BsonBinaryData onNull) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); @@ -139,7 +139,7 @@ public static BsonBinaryData ConvertToBinData(long? value, BsonBinarySubType sub /// The BsonBinaryData subtype of the result value. /// The byte ordering of BsonBinaryData. /// The converted value. - public static BsonBinaryData ConvertToBinData(double? value, BsonBinarySubType subtype, ByteOrder byteOrder) + public static BsonBinaryData ToBinData(double? value, BsonBinarySubType subtype, ByteOrder byteOrder) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } @@ -154,7 +154,7 @@ public static BsonBinaryData ConvertToBinData(double? value, BsonBinarySubType s /// The onNull value. /// The converted value. /// - public static BsonBinaryData ConvertToBinData(double? value, BsonBinarySubType subtype, ByteOrder byteOrder, + public static BsonBinaryData ToBinData(double? value, BsonBinarySubType subtype, ByteOrder byteOrder, BsonBinaryData onError, BsonBinaryData onNull) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); @@ -166,7 +166,7 @@ public static BsonBinaryData ConvertToBinData(double? value, BsonBinarySubType s /// The value. /// The format string. /// The converted value. - public static string ConvertToString(BsonBinaryData value, string format) + public static string ToString(BsonBinaryData value, string format) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } @@ -179,7 +179,7 @@ public static string ConvertToString(BsonBinaryData value, string format) /// The onError value. /// The onNull value. /// The converted value. - public static string ConvertToString(BsonBinaryData value, string format, string onError, string onNull) + public static string ToString(BsonBinaryData value, string format, string onError, string onNull) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } @@ -190,7 +190,7 @@ public static string ConvertToString(BsonBinaryData value, string format, string /// The value. /// The byte ordering of BsonBinaryData. /// The converted value. - public static int? ConvertToNullableInt(BsonBinaryData value, ByteOrder byteOrder) + public static int? ToNullableInt(BsonBinaryData value, ByteOrder byteOrder) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } @@ -203,7 +203,7 @@ public static string ConvertToString(BsonBinaryData value, string format, string /// The onError value. /// The onNull value. /// The converted value. - public static int? ConvertToNullableInt(BsonBinaryData value, ByteOrder byteOrder, int? onError, int? onNull) + public static int? ToNullableInt(BsonBinaryData value, ByteOrder byteOrder, int? onError, int? onNull) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } @@ -214,7 +214,7 @@ public static string ConvertToString(BsonBinaryData value, string format, string /// The value. /// The byte ordering of BsonBinaryData. /// The converted value. - public static long? ConvertToNullableLong(BsonBinaryData value, ByteOrder byteOrder) + public static long? ToNullableLong(BsonBinaryData value, ByteOrder byteOrder) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } @@ -227,7 +227,7 @@ public static string ConvertToString(BsonBinaryData value, string format, string /// The onError value. /// The onNull value. /// The converted value. - public static long? ConvertToNullableLong(BsonBinaryData value, ByteOrder byteOrder, long? onError, long? onNull) + public static long? ToNullableLong(BsonBinaryData value, ByteOrder byteOrder, long? onError, long? onNull) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } @@ -238,7 +238,7 @@ public static string ConvertToString(BsonBinaryData value, string format, string /// The value. /// The byte ordering of BsonBinaryData. /// The converted value. - public static double? ConvertToNullableDouble(BsonBinaryData value, ByteOrder byteOrder) + public static double? ToNullableDouble(BsonBinaryData value, ByteOrder byteOrder) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } @@ -251,7 +251,7 @@ public static string ConvertToString(BsonBinaryData value, string format, string /// The onError value. /// The onNull value. /// The converted value. - public static double? ConvertToNullableDouble(BsonBinaryData value, ByteOrder byteOrder, double? onError, double? onNull) + public static double? ToNullableDouble(BsonBinaryData value, ByteOrder byteOrder, double? onError, double? onNull) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs index 699ba7fabbd..c67218d197c 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs @@ -47,7 +47,7 @@ public void MongoDBFunctions_ConvertToBinDataFromDouble_should_work(int id, Byte var collection = Fixture.Collection; var queryable = collection.AsQueryable() .Where(x => x.Id == id) - .Select(x => Mql.ConvertToBinData(x.DoubleProperty, BsonBinarySubType.Binary, byteOrder)); + .Select(x => Mql.ToBinData(x.DoubleProperty, BsonBinarySubType.Binary, byteOrder)); var expectedStages = new[] @@ -75,7 +75,7 @@ public void MongoDBFunctions_ConvertToBinDataFromDoubleWithOnErrorAndOnNull_shou var collection = Fixture.Collection; var queryable = collection.AsQueryable() .Where(x => x.Id == id) - .Select(x => Mql.ConvertToBinData(x.DoubleProperty, BsonBinarySubType.Binary, byteOrder, onErrorBinData, onNullBinData)); + .Select(x => Mql.ToBinData(x.DoubleProperty, BsonBinarySubType.Binary, byteOrder, onErrorBinData, onNullBinData)); var onErrorString = onErrorBase64 == null ? "null" : $"BinData(0, '{onErrorBase64}')"; var onNullString = onNullBase64 == null ? "null" : $"BinData(0, '{onNullBase64}')"; @@ -101,7 +101,7 @@ public void MongoDBFunctions_ConvertToBinDataFromInt_should_work(int id, ByteOrd var collection = Fixture.Collection; var queryable = collection.AsQueryable() .Where(x => x.Id == id) - .Select(x => Mql.ConvertToBinData(x.IntProperty, BsonBinarySubType.Binary, byteOrder)); + .Select(x => Mql.ToBinData(x.IntProperty, BsonBinarySubType.Binary, byteOrder)); var expectedStages = new[] @@ -129,7 +129,7 @@ public void MongoDBFunctions_ConvertToBinDataFromIntWithOnErrorAndOnNull_should_ var collection = Fixture.Collection; var queryable = collection.AsQueryable() .Where(x => x.Id == id) - .Select(x => Mql.ConvertToBinData(x.IntProperty, BsonBinarySubType.Binary, byteOrder, onErrorBinData, onNullBinData)); + .Select(x => Mql.ToBinData(x.IntProperty, BsonBinarySubType.Binary, byteOrder, onErrorBinData, onNullBinData)); var onErrorString = onErrorBase64 == null ? "null" : $"BinData(0, '{onErrorBase64}')"; var onNullString = onNullBase64 == null ? "null" : $"BinData(0, '{onNullBase64}')"; @@ -155,7 +155,7 @@ public void MongoDBFunctions_ConvertToLongDataFromInt_should_work(int id, ByteOr var collection = Fixture.Collection; var queryable = collection.AsQueryable() .Where(x => x.Id == id) - .Select(x => Mql.ConvertToBinData(x.LongProperty, BsonBinarySubType.Binary, byteOrder)); + .Select(x => Mql.ToBinData(x.LongProperty, BsonBinarySubType.Binary, byteOrder)); var expectedStages = new[] @@ -191,7 +191,7 @@ public void MongoDBFunctions_ConvertToLongDataFromIntWithOnErrorAndOnNull_should var collection = Fixture.Collection; var queryable = collection.AsQueryable() .Where(x => x.Id == id) - .Select(x => Mql.ConvertToBinData(x.LongProperty, BsonBinarySubType.Binary, byteOrder, onErrorBinData, onNullBinData)); + .Select(x => Mql.ToBinData(x.LongProperty, BsonBinarySubType.Binary, byteOrder, onErrorBinData, onNullBinData)); var onErrorString = onErrorBase64 == null ? "null" : $"BinData(0, '{onErrorBase64}')"; var onNullString = onNullBase64 == null ? "null" : $"BinData(0, '{onNullBase64}')"; @@ -216,7 +216,7 @@ public void MongoDBFunctions_ConvertToBinDataFromString_should_work(int id, stri var collection = Fixture.Collection; var queryable = collection.AsQueryable() .Where(x => x.Id == id) - .Select(x => Mql.ConvertToBinData(x.StringProperty, BsonBinarySubType.UuidStandard, "uuid")); + .Select(x => Mql.ToBinData(x.StringProperty, BsonBinarySubType.UuidStandard, "uuid")); var expectedStages = new[] @@ -242,7 +242,7 @@ public void MongoDBFunctions_ConvertToBinDataFromStringWithOnErrorAndOnNull_shou var collection = Fixture.Collection; var queryable = collection.AsQueryable() .Where(x => x.Id == id) - .Select(x => Mql.ConvertToBinData(x.StringProperty, BsonBinarySubType.UuidStandard, "uuid", onErrorBinData, onNullBinData)); + .Select(x => Mql.ToBinData(x.StringProperty, BsonBinarySubType.UuidStandard, "uuid", onErrorBinData, onNullBinData)); var onErrorString = onErrorBase64 == null ? "null" : $"BinData(0, '{onErrorBase64}')"; var onNullString = onNullBase64 == null ? "null" : $"BinData(0, '{onNullBase64}')"; @@ -271,7 +271,7 @@ public void MongoDBFunctions_ConvertToDoubleFromBinData_should_work(int id, Byte var collection = Fixture.Collection; var queryable = collection.AsQueryable() .Where(x => x.Id == id) - .Select(x => Mql.ConvertToNullableDouble(x.BinaryProperty, byteOrder)); + .Select(x => Mql.ToNullableDouble(x.BinaryProperty, byteOrder)); var expectedStages = new[] @@ -297,7 +297,7 @@ public void MongoDBFunctions_ConvertToDoubleFromBinDataWithOnErrorAndOnNull_shou var collection = Fixture.Collection; var queryable = collection.AsQueryable() .Where(x => x.Id == id) - .Select(x => Mql.ConvertToNullableDouble(x.BinaryProperty, byteOrder, onError, onNull)); + .Select(x => Mql.ToNullableDouble(x.BinaryProperty, byteOrder, onError, onNull)); var onErrorString = onError == null ? "null" : onError.Value.ToString(NumberFormatInfo.InvariantInfo); var onNullString = onNull == null ? "null" : onNull.Value.ToString(NumberFormatInfo.InvariantInfo); @@ -324,7 +324,7 @@ public void MongoDBFunctions_ConvertToIntFromBinData_should_work(int id, ByteOrd var collection = Fixture.Collection; var queryable = collection.AsQueryable() .Where(x => x.Id == id) - .Select(x => Mql.ConvertToNullableInt(x.BinaryProperty, byteOrder)); + .Select(x => Mql.ToNullableInt(x.BinaryProperty, byteOrder)); var expectedStages = new[] @@ -350,7 +350,7 @@ public void MongoDBFunctions_ConvertToIntFromBinDataWithOnErrorAndOnNull_should_ var collection = Fixture.Collection; var queryable = collection.AsQueryable() .Where(x => x.Id == id) - .Select(x => Mql.ConvertToNullableInt(x.BinaryProperty, byteOrder, onError, onNull)); + .Select(x => Mql.ToNullableInt(x.BinaryProperty, byteOrder, onError, onNull)); var onErrorString = onError == null ? "null" : onError.Value.ToString(NumberFormatInfo.InvariantInfo); var onNullString = onNull == null ? "null" : onNull.Value.ToString(NumberFormatInfo.InvariantInfo); @@ -377,7 +377,7 @@ public void MongoDBFunctions_ConvertToLongFromBinData_should_work(int id, ByteOr var collection = Fixture.Collection; var queryable = collection.AsQueryable() .Where(x => x.Id == id) - .Select(x => Mql.ConvertToNullableLong(x.BinaryProperty, byteOrder)); + .Select(x => Mql.ToNullableLong(x.BinaryProperty, byteOrder)); var expectedStages = new[] @@ -403,7 +403,7 @@ public void MongoDBFunctions_ConvertToLongFromBinDataWithOnErrorAndOnNull_should var collection = Fixture.Collection; var queryable = collection.AsQueryable() .Where(x => x.Id == id) - .Select(x => Mql.ConvertToNullableLong(x.BinaryProperty, byteOrder, onError, onNull)); + .Select(x => Mql.ToNullableLong(x.BinaryProperty, byteOrder, onError, onNull)); var onErrorString = onError == null ? "null" : onError.Value.ToString(NumberFormatInfo.InvariantInfo); var onNullString = onNull == null ? "null" : onNull.Value.ToString(NumberFormatInfo.InvariantInfo); @@ -429,7 +429,7 @@ public void MongoDBFunctions_ConvertToStringFromBinData_should_work(int id, stri var collection = Fixture.Collection; var queryable = collection.AsQueryable() .Where(x => x.Id == id) - .Select(x => Mql.ConvertToString(x.BinaryProperty, "uuid")); + .Select(x => Mql.ToString(x.BinaryProperty, "uuid")); var expectedStages = new[] @@ -453,7 +453,7 @@ public void MongoDBFunctions_ConvertToStringFromBinDataWithOnErrorAndOnNull_shou var collection = Fixture.Collection; var queryable = collection.AsQueryable() .Where(x => x.Id == id) - .Select(x => Mql.ConvertToString(x.BinaryProperty, "uuid", onError, onNull)); + .Select(x => Mql.ToString(x.BinaryProperty, "uuid", onError, onNull)); var onErrorString = onError == null ? "null" : $"'{onError}'"; var onNullString = onNull == null ? "null" : $"'{onNull}'"; From 2b16ebbc788c3d5783463693faa472f2b6ccdee2 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Wed, 26 Mar 2025 10:19:23 +0100 Subject: [PATCH 32/82] Corrected name of feature --- src/MongoDB.Driver/Core/Misc/Feature.cs | 8 ++--- ...dToAggregationExpressionTranslatorTests.cs | 32 +++++++++---------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/MongoDB.Driver/Core/Misc/Feature.cs b/src/MongoDB.Driver/Core/Misc/Feature.cs index 35496cc378b..c8785f2e027 100644 --- a/src/MongoDB.Driver/Core/Misc/Feature.cs +++ b/src/MongoDB.Driver/Core/Misc/Feature.cs @@ -44,8 +44,8 @@ public class Feature private static readonly Feature __clientBulkWrite = new Feature("ClientBulkWrite", WireVersion.Server80); private static readonly Feature __clientSideEncryption = new Feature("ClientSideEncryption", WireVersion.Server42); private static readonly Feature __clusteredIndexes = new Feature("ClusteredIndexes", WireVersion.Server53); - private static readonly Feature __convertBinDataToFromNumeric = new Feature("ConvertBinDataToFromNumeric", WireVersion.Server81); - private static readonly Feature __convertBinDataToFromString= new Feature("ConvertBinDataToFromString", WireVersion.Server80); + private static readonly Feature __convertOperatorBinDataToFromNumeric = new Feature("ConvertBinDataToFromNumeric", WireVersion.Server81); + private static readonly Feature __convertOperatorBinDataToFromString= new Feature("ConvertBinDataToFromString", WireVersion.Server80); private static readonly Feature __createIndexCommitQuorum = new Feature("CreateIndexCommitQuorum", WireVersion.Server44); private static readonly Feature __createIndexesUsingInsertOperations = new Feature("CreateIndexesUsingInsertOperations", WireVersion.Zero, WireVersion.Server42); private static readonly Feature __csfleRangeAlgorithm = new Feature("CsfleRangeAlgorithm", WireVersion.Server62); @@ -198,12 +198,12 @@ public class Feature /// /// Gets the conversion of binary data to/from numeric types feature. /// - public static Feature ConvertBinDataToFromNumeric => __convertBinDataToFromNumeric; + public static Feature ConvertOperatorBinDataToFromNumeric => __convertOperatorBinDataToFromNumeric; /// /// Gets the conversion of binary data to/from string feature. /// - public static Feature ConvertBinDataToFromString => __convertBinDataToFromString; + public static Feature ConvertOperatorBinDataToFromString => __convertOperatorBinDataToFromString; /// /// Gets the create index commit quorum feature. diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs index c67218d197c..adb77dec736 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs @@ -42,7 +42,7 @@ public ConvertMethodToAggregationExpressionTranslatorTests(ClassFixture fixture) [InlineData(5, ByteOrder.BigEndian, "wAQAAAAAAAA=", null )] public void MongoDBFunctions_ConvertToBinDataFromDouble_should_work(int id, ByteOrder byteOrder, string expectedBase64, string expectedException) { - RequireServer.Check().Supports(Feature.ConvertBinDataToFromNumeric); + RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); var collection = Fixture.Collection; var queryable = collection.AsQueryable() @@ -67,7 +67,7 @@ public void MongoDBFunctions_ConvertToBinDataFromDouble_should_work(int id, Byte [InlineData(10, ByteOrder.BigEndian, "Ag==", "Ag==", "AAAAAAAABMA=")] public void MongoDBFunctions_ConvertToBinDataFromDoubleWithOnErrorAndOnNull_should_work(int id, ByteOrder byteOrder, string expectedBase64, string onErrorBase64, string onNullBase64) { - RequireServer.Check().Supports(Feature.ConvertBinDataToFromNumeric); + RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); var onErrorBinData = onErrorBase64 == null ? null : new BsonBinaryData(Convert.FromBase64String(onErrorBase64)); var onNullBinData = onNullBase64 == null ? null : new BsonBinaryData(Convert.FromBase64String(onNullBase64)); @@ -96,7 +96,7 @@ public void MongoDBFunctions_ConvertToBinDataFromDoubleWithOnErrorAndOnNull_shou [InlineData(6, ByteOrder.BigEndian, "AAAAKg==", null )] public void MongoDBFunctions_ConvertToBinDataFromInt_should_work(int id, ByteOrder byteOrder, string expectedBase64, string expectedException) { - RequireServer.Check().Supports(Feature.ConvertBinDataToFromNumeric); + RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); var collection = Fixture.Collection; var queryable = collection.AsQueryable() @@ -121,7 +121,7 @@ public void MongoDBFunctions_ConvertToBinDataFromInt_should_work(int id, ByteOrd [InlineData(10, ByteOrder.BigEndian, "Ag==", "Ag==", "AAAAAAAABMA=")] public void MongoDBFunctions_ConvertToBinDataFromIntWithOnErrorAndOnNull_should_work(int id, ByteOrder byteOrder, string expectedBase64, string onErrorBase64, string onNullBase64) { - RequireServer.Check().Supports(Feature.ConvertBinDataToFromNumeric); + RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); var onErrorBinData = onErrorBase64 == null ? null : new BsonBinaryData(Convert.FromBase64String(onErrorBase64)); var onNullBinData = onNullBase64 == null ? null : new BsonBinaryData(Convert.FromBase64String(onNullBase64)); @@ -150,7 +150,7 @@ public void MongoDBFunctions_ConvertToBinDataFromIntWithOnErrorAndOnNull_should_ [InlineData(6, ByteOrder.BigEndian, "AAAAKg==", null )] public void MongoDBFunctions_ConvertToLongDataFromInt_should_work(int id, ByteOrder byteOrder, string expectedBase64, string expectedException) { - RequireServer.Check().Supports(Feature.ConvertBinDataToFromNumeric); + RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); var collection = Fixture.Collection; var queryable = collection.AsQueryable() @@ -183,7 +183,7 @@ public void MongoDBFunctions_ConvertToLongDataFromInt_should_work(int id, ByteOr [InlineData(10, ByteOrder.BigEndian, "Ag==", "Ag==", "AAAAAAAABMA=")] public void MongoDBFunctions_ConvertToLongDataFromIntWithOnErrorAndOnNull_should_work(int id, ByteOrder byteOrder, string expectedBase64, string onErrorBase64, string onNullBase64) { - RequireServer.Check().Supports(Feature.ConvertBinDataToFromNumeric); + RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); var onErrorBinData = onErrorBase64 == null ? null : new BsonBinaryData(Convert.FromBase64String(onErrorBase64)); var onNullBinData = onNullBase64 == null ? null : new BsonBinaryData(Convert.FromBase64String(onNullBase64)); @@ -211,7 +211,7 @@ public void MongoDBFunctions_ConvertToLongDataFromIntWithOnErrorAndOnNull_should [InlineData(2, "867dee52-c331-484e-92d1-c56479b8e67e", null)] public void MongoDBFunctions_ConvertToBinDataFromString_should_work(int id, string expectedGuidString, string expectedException) { - RequireServer.Check().Supports(Feature.ConvertBinDataToFromString); + RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromString); var collection = Fixture.Collection; var queryable = collection.AsQueryable() @@ -234,7 +234,7 @@ public void MongoDBFunctions_ConvertToBinDataFromString_should_work(int id, stri [InlineData(10, "Ag==", "Ag==", "AAAAAAAABMA=")] public void MongoDBFunctions_ConvertToBinDataFromStringWithOnErrorAndOnNull_should_work(int id, string expectedBase64, string onErrorBase64, string onNullBase64) { - RequireServer.Check().Supports(Feature.ConvertBinDataToFromNumeric); + RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); var onErrorBinData = onErrorBase64 == null ? null : new BsonBinaryData(Convert.FromBase64String(onErrorBase64)); var onNullBinData = onNullBase64 == null ? null : new BsonBinaryData(Convert.FromBase64String(onNullBase64)); @@ -266,7 +266,7 @@ public void MongoDBFunctions_ConvertToBinDataFromStringWithOnErrorAndOnNull_shou [InlineData(5, ByteOrder.BigEndian, -2.5, null)] public void MongoDBFunctions_ConvertToDoubleFromBinData_should_work(int id, ByteOrder byteOrder, double? expectedResult, string expectedException) { - RequireServer.Check().Supports(Feature.ConvertBinDataToFromNumeric); + RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); var collection = Fixture.Collection; var queryable = collection.AsQueryable() @@ -292,7 +292,7 @@ public void MongoDBFunctions_ConvertToDoubleFromBinData_should_work(int id, Byte [InlineData(0, ByteOrder.LittleEndian, null, 15.2, null)] public void MongoDBFunctions_ConvertToDoubleFromBinDataWithOnErrorAndOnNull_should_work(int id, ByteOrder byteOrder, double? expectedResult, double? onError, double? onNull) { - RequireServer.Check().Supports(Feature.ConvertBinDataToFromNumeric); + RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); var collection = Fixture.Collection; var queryable = collection.AsQueryable() @@ -319,7 +319,7 @@ public void MongoDBFunctions_ConvertToDoubleFromBinDataWithOnErrorAndOnNull_shou [InlineData(6, ByteOrder.BigEndian, 42, null)] public void MongoDBFunctions_ConvertToIntFromBinData_should_work(int id, ByteOrder byteOrder, int? expectedResult, string expectedException) { - RequireServer.Check().Supports(Feature.ConvertBinDataToFromNumeric); + RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); var collection = Fixture.Collection; var queryable = collection.AsQueryable() @@ -345,7 +345,7 @@ public void MongoDBFunctions_ConvertToIntFromBinData_should_work(int id, ByteOrd [InlineData(0, ByteOrder.LittleEndian, null, 15, null)] public void MongoDBFunctions_ConvertToIntFromBinDataWithOnErrorAndOnNull_should_work(int id, ByteOrder byteOrder, int? expectedResult, int? onError, int? onNull) { - RequireServer.Check().Supports(Feature.ConvertBinDataToFromNumeric); + RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); var collection = Fixture.Collection; var queryable = collection.AsQueryable() @@ -372,7 +372,7 @@ public void MongoDBFunctions_ConvertToIntFromBinDataWithOnErrorAndOnNull_should_ [InlineData(6, ByteOrder.BigEndian, (long)42, null)] public void MongoDBFunctions_ConvertToLongFromBinData_should_work(int id, ByteOrder byteOrder, long? expectedResult, string expectedException) { - RequireServer.Check().Supports(Feature.ConvertBinDataToFromNumeric); + RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); var collection = Fixture.Collection; var queryable = collection.AsQueryable() @@ -398,7 +398,7 @@ public void MongoDBFunctions_ConvertToLongFromBinData_should_work(int id, ByteOr [InlineData(0, ByteOrder.LittleEndian, null, (long)15, null)] public void MongoDBFunctions_ConvertToLongFromBinDataWithOnErrorAndOnNull_should_work(int id, ByteOrder byteOrder, long? expectedResult, long? onError, long? onNull) { - RequireServer.Check().Supports(Feature.ConvertBinDataToFromNumeric); + RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); var collection = Fixture.Collection; var queryable = collection.AsQueryable() @@ -424,7 +424,7 @@ public void MongoDBFunctions_ConvertToLongFromBinDataWithOnErrorAndOnNull_should [InlineData(1, null, "MongoCommandException")] public void MongoDBFunctions_ConvertToStringFromBinData_should_work(int id, string expectedResult, string expectedException) { - RequireServer.Check().Supports(Feature.ConvertBinDataToFromString); + RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromString); var collection = Fixture.Collection; var queryable = collection.AsQueryable() @@ -448,7 +448,7 @@ public void MongoDBFunctions_ConvertToStringFromBinData_should_work(int id, stri [InlineData(1, null, null, "onNull")] public void MongoDBFunctions_ConvertToStringFromBinDataWithOnErrorAndOnNull_should_work(int id, string expectedResult, string onError, string onNull) { - RequireServer.Check().Supports(Feature.ConvertBinDataToFromString); + RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromString); var collection = Fixture.Collection; var queryable = collection.AsQueryable() From 40276edd707dc8a942b0f956d0f4588bf03f36a6 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Wed, 26 Mar 2025 10:21:36 +0100 Subject: [PATCH 33/82] Small corrections --- .../Ast/Expressions/AstConvertExpression.cs | 6 +++--- .../Ast/Expressions/AstExpression.cs | 11 ++++++++--- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstConvertExpression.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstConvertExpression.cs index 95a31218ecd..14b73f5ea6e 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstConvertExpression.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstConvertExpression.cs @@ -40,11 +40,11 @@ public AstConvertExpression( { _input = Ensure.IsNotNull(input, nameof(input)); _to = Ensure.IsNotNull(to, nameof(to)); - _onError = onError; - _onNull = onNull; _subType = subType; - _format = format; _byteOrder = byteOrder; + _format = format; + _onError = onError; + _onNull = onNull; } public ByteOrder? ByteOrder => _byteOrder; diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstExpression.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstExpression.cs index 7126a165873..8de612f95f1 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstExpression.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstExpression.cs @@ -255,9 +255,14 @@ public static AstExpression Constant(BsonValue value) return new AstConstantExpression(value); } - public static AstExpression Convert(AstExpression input, AstExpression to, - BsonBinarySubType? subType = null, ByteOrder? byteOrder = null, string format = null, - AstExpression onError = null, AstExpression onNull = null) + public static AstExpression Convert( + AstExpression input, + AstExpression to, + BsonBinarySubType? subType = null, + ByteOrder? byteOrder = null, + string format = null, + AstExpression onError = null, + AstExpression onNull = null) { Ensure.IsNotNull(input, nameof(input)); Ensure.IsNotNull(to, nameof(to)); From 16887f906b1ed9db0ae2d17bc031b5bd3c0d433f Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Wed, 26 Mar 2025 10:38:09 +0100 Subject: [PATCH 34/82] Added convert options --- .../Reflection/MqlMethod.cs | 16 +-- src/MongoDB.Driver/Mql.cs | 98 ++++++++++++------- ...dToAggregationExpressionTranslatorTests.cs | 16 +-- 3 files changed, 80 insertions(+), 50 deletions(-) diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/MqlMethod.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/MqlMethod.cs index cd3c2a9aa59..cd1ed032276 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/MqlMethod.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/MqlMethod.cs @@ -66,18 +66,18 @@ static MqlMethod() __isNullOrMissing = ReflectionInfo.Method((object field) => Mql.IsNullOrMissing(field)); // Convert methods - __toBinDataFromDouble = ReflectionInfo.Method((double? field, BsonBinarySubType subType, ByteOrder byteOrder) => Mql.ToBinData(field, subType, byteOrder)); + __toBinDataFromDouble = ReflectionInfo.Method((double? field, BsonBinarySubType subType, ByteOrder byteOrder) => Mql.ToBsonBinaryData(field, subType, byteOrder)); __toBinDataFromDoubleWithOnErrorAndOnNull = ReflectionInfo.Method((double? field, BsonBinarySubType subType, ByteOrder byteOrder, BsonBinaryData onError, BsonBinaryData onNull) - => Mql.ToBinData(field, subType, byteOrder, onError, onNull)); - __toBinDataFromInt = ReflectionInfo.Method((int? field, BsonBinarySubType subType, ByteOrder byteOrder) => Mql.ToBinData(field, subType, byteOrder)); + => Mql.ToBsonBinaryData(field, subType, byteOrder, onError, onNull)); + __toBinDataFromInt = ReflectionInfo.Method((int? field, BsonBinarySubType subType, ByteOrder byteOrder) => Mql.ToBsonBinaryData(field, subType, byteOrder)); __toBinDataFromIntWithOnErrorAndOnNull = ReflectionInfo.Method((int? field, BsonBinarySubType subType, ByteOrder byteOrder, BsonBinaryData onError, BsonBinaryData onNull) - => Mql.ToBinData(field, subType, byteOrder, onError, onNull)); - __toBinDataFromLong = ReflectionInfo.Method((long? field, BsonBinarySubType subType, ByteOrder byteOrder) => Mql.ToBinData(field, subType, byteOrder)); + => Mql.ToBsonBinaryData(field, subType, byteOrder, onError, onNull)); + __toBinDataFromLong = ReflectionInfo.Method((long? field, BsonBinarySubType subType, ByteOrder byteOrder) => Mql.ToBsonBinaryData(field, subType, byteOrder)); __toBinDataFromLongWithOnErrorAndOnNull = ReflectionInfo.Method((long? field, BsonBinarySubType subType, ByteOrder byteOrder, BsonBinaryData onError, BsonBinaryData onNull) - => Mql.ToBinData(field, subType, byteOrder, onError, onNull)); - __toBinDataFromString = ReflectionInfo.Method((string field, BsonBinarySubType subType, string format) => Mql.ToBinData(field, subType, format)); + => Mql.ToBsonBinaryData(field, subType, byteOrder, onError, onNull)); + __toBinDataFromString = ReflectionInfo.Method((string field, BsonBinarySubType subType, string format) => Mql.ToBsonBinaryData(field, subType, format)); __toBinDataFromStringWithOnErrorAndOnNull = ReflectionInfo.Method((string field, BsonBinarySubType subType, string format, BsonBinaryData onError, BsonBinaryData onNull) - => Mql.ToBinData(field, subType, format, onError, onNull)); + => Mql.ToBsonBinaryData(field, subType, format, onError, onNull)); __toNullableDoubleFromBinData = ReflectionInfo.Method((BsonBinaryData field, ByteOrder byteOrder) => Mql.ToNullableDouble(field, byteOrder)); __toNullableDoubleFromBinDataWithOnErrorAndOnNull = ReflectionInfo.Method((BsonBinaryData field, ByteOrder byteOrder, double? onError, double? onNull) => Mql.ToNullableDouble(field, byteOrder, onError, onNull)); diff --git a/src/MongoDB.Driver/Mql.cs b/src/MongoDB.Driver/Mql.cs index c5f87b05190..f8ddbd8d950 100644 --- a/src/MongoDB.Driver/Mql.cs +++ b/src/MongoDB.Driver/Mql.cs @@ -56,34 +56,32 @@ public static TValue Constant(TValue value, BsonType representaion) /// The BsonBinaryData subtype of the result value. /// The format string. /// The converted value. - public static BsonBinaryData ToBinData(string value, BsonBinarySubType subtype, string format) + public static BsonValue ToBsonBinaryData(string value, BsonBinarySubType subtype, string format) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } /// - /// Converts a string to a BsonBinaryData using the $convert aggregation operator. + /// Converts a string to BsonBinaryData using the $convert aggregation operator. /// /// The value. /// The BsonBinaryData subtype of the result value. /// The format string. - /// The onError value. - /// The onNull value. + /// The convert options. /// The converted value. - public static BsonBinaryData ToBinData(string value, BsonBinarySubType subtype, string format, - BsonBinaryData onError, BsonBinaryData onNull) + public static BsonValue ToBsonBinaryData(string value, BsonBinarySubType subtype, string format, ConvertOptions options) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } /// - /// Converts an int to a BsonBinaryData using the $convert aggregation operator. + /// Converts an int to BsonBinaryData using the $convert aggregation operator. /// /// The value. /// The BsonBinaryData subtype of the result value. /// The byte order of BsonBinaryData. /// The converted value. - public static BsonBinaryData ToBinData(int? value, BsonBinarySubType subtype, ByteOrder byteOrder) + public static BsonValue ToBsonBinaryData(int? value, BsonBinarySubType subtype, ByteOrder byteOrder) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } @@ -94,12 +92,10 @@ public static BsonBinaryData ToBinData(int? value, BsonBinarySubType subtype, By /// The value. /// The BsonBinaryData subtype of the result value. /// The byte ordering of BsonBinaryData. - /// The onError value. - /// The onNull value. + /// The convert options. /// The converted value. /// - public static BsonBinaryData ToBinData(int? value, BsonBinarySubType subtype, ByteOrder byteOrder, - BsonBinaryData onError, BsonBinaryData onNull) + public static BsonValue ToBsonBinaryData(int? value, BsonBinarySubType subtype, ByteOrder byteOrder, ConvertOptions options) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } @@ -111,7 +107,7 @@ public static BsonBinaryData ToBinData(int? value, BsonBinarySubType subtype, By /// The BsonBinaryData subtype of the result value. /// The byte ordering of BsonBinaryData. /// The converted value. - public static BsonBinaryData ToBinData(long? value, BsonBinarySubType subtype, ByteOrder byteOrder) + public static BsonValue ToBsonBinaryData(long? value, BsonBinarySubType subtype, ByteOrder byteOrder) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } @@ -122,12 +118,10 @@ public static BsonBinaryData ToBinData(long? value, BsonBinarySubType subtype, B /// The value. /// The BsonBinaryData subtype of the result value. /// The byte ordering of BsonBinaryData. - /// The onError value. - /// The onNull value. + /// The convert options. /// The converted value. /// - public static BsonBinaryData ToBinData(long? value, BsonBinarySubType subtype, ByteOrder byteOrder, - BsonBinaryData onError, BsonBinaryData onNull) + public static BsonValue ToBsonBinaryData(long? value, BsonBinarySubType subtype, ByteOrder byteOrder, ConvertOptions options) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } @@ -139,7 +133,7 @@ public static BsonBinaryData ToBinData(long? value, BsonBinarySubType subtype, B /// The BsonBinaryData subtype of the result value. /// The byte ordering of BsonBinaryData. /// The converted value. - public static BsonBinaryData ToBinData(double? value, BsonBinarySubType subtype, ByteOrder byteOrder) + public static BsonValue ToBsonBinaryData(double? value, BsonBinarySubType subtype, ByteOrder byteOrder) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } @@ -150,12 +144,10 @@ public static BsonBinaryData ToBinData(double? value, BsonBinarySubType subtype, /// The field. /// The BsonBinaryData subtype of the result value. /// The byte ordering of BsonBinaryData. - /// The onError value. - /// The onNull value. + /// The convert options. /// The converted value. /// - public static BsonBinaryData ToBinData(double? value, BsonBinarySubType subtype, ByteOrder byteOrder, - BsonBinaryData onError, BsonBinaryData onNull) + public static BsonValue ToBsonBinaryData(double? value, BsonBinarySubType subtype, ByteOrder byteOrder, ConvertOptions options) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } @@ -176,10 +168,9 @@ public static string ToString(BsonBinaryData value, string format) /// /// The value. /// The format string. - /// The onError value. - /// The onNull value. + /// The convert options. /// The converted value. - public static string ToString(BsonBinaryData value, string format, string onError, string onNull) + public static string ToString(BsonBinaryData value, string format, ConvertOptions options) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } @@ -200,10 +191,9 @@ public static string ToString(BsonBinaryData value, string format, string onErro /// /// The value. /// The byte ordering of BsonBinaryData. - /// The onError value. - /// The onNull value. + /// The convert options. /// The converted value. - public static int? ToNullableInt(BsonBinaryData value, ByteOrder byteOrder, int? onError, int? onNull) + public static int? ToNullableInt(BsonBinaryData value, ByteOrder byteOrder, ConvertOptions options) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } @@ -224,10 +214,9 @@ public static string ToString(BsonBinaryData value, string format, string onErro /// /// The value. /// The byte ordering of BsonBinaryData. - /// The onError value. - /// The onNull value. + /// The convert options. /// The converted value. - public static long? ToNullableLong(BsonBinaryData value, ByteOrder byteOrder, long? onError, long? onNull) + public static long? ToNullableLong(BsonBinaryData value, ByteOrder byteOrder, ConvertOptions options) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } @@ -248,10 +237,9 @@ public static string ToString(BsonBinaryData value, string format, string onErro /// /// The value. /// The byte ordering of BsonBinaryData. - /// The onError value. - /// The onNull value. + /// The convert options. /// The converted value. - public static double? ToNullableDouble(BsonBinaryData value, ByteOrder byteOrder, double? onError, double? onNull) + public static double? ToNullableDouble(BsonBinaryData value, ByteOrder byteOrder, ConvertOptions options) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } @@ -375,4 +363,46 @@ public enum ByteOrder /// LittleEndian, } + + //TODO Add docs + /// + /// + /// + /// + public class ConvertOptions + { + private TResult _onError; + private bool _onErrorWasSet; + private TResult _onNull; + private bool _onNullWasSet; + + /// + /// + /// + public TResult OnError + { + get => _onError; + set + { + _onError = value; + _onErrorWasSet = true; + } + } + + /// + /// + /// + public TResult OnNull + { + get => _onNull; + set + { + _onNull = value; + _onNullWasSet = true; + } + } + + internal bool OnErrorWasSet => _onErrorWasSet; + internal bool OnNullWasSet => _onNullWasSet; + } } diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs index adb77dec736..19f9d8f6527 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs @@ -47,7 +47,7 @@ public void MongoDBFunctions_ConvertToBinDataFromDouble_should_work(int id, Byte var collection = Fixture.Collection; var queryable = collection.AsQueryable() .Where(x => x.Id == id) - .Select(x => Mql.ToBinData(x.DoubleProperty, BsonBinarySubType.Binary, byteOrder)); + .Select(x => Mql.ToBsonBinaryData(x.DoubleProperty, BsonBinarySubType.Binary, byteOrder)); var expectedStages = new[] @@ -75,7 +75,7 @@ public void MongoDBFunctions_ConvertToBinDataFromDoubleWithOnErrorAndOnNull_shou var collection = Fixture.Collection; var queryable = collection.AsQueryable() .Where(x => x.Id == id) - .Select(x => Mql.ToBinData(x.DoubleProperty, BsonBinarySubType.Binary, byteOrder, onErrorBinData, onNullBinData)); + .Select(x => Mql.ToBsonBinaryData(x.DoubleProperty, BsonBinarySubType.Binary, byteOrder, onErrorBinData, onNullBinData)); var onErrorString = onErrorBase64 == null ? "null" : $"BinData(0, '{onErrorBase64}')"; var onNullString = onNullBase64 == null ? "null" : $"BinData(0, '{onNullBase64}')"; @@ -101,7 +101,7 @@ public void MongoDBFunctions_ConvertToBinDataFromInt_should_work(int id, ByteOrd var collection = Fixture.Collection; var queryable = collection.AsQueryable() .Where(x => x.Id == id) - .Select(x => Mql.ToBinData(x.IntProperty, BsonBinarySubType.Binary, byteOrder)); + .Select(x => Mql.ToBsonBinaryData(x.IntProperty, BsonBinarySubType.Binary, byteOrder)); var expectedStages = new[] @@ -129,7 +129,7 @@ public void MongoDBFunctions_ConvertToBinDataFromIntWithOnErrorAndOnNull_should_ var collection = Fixture.Collection; var queryable = collection.AsQueryable() .Where(x => x.Id == id) - .Select(x => Mql.ToBinData(x.IntProperty, BsonBinarySubType.Binary, byteOrder, onErrorBinData, onNullBinData)); + .Select(x => Mql.ToBsonBinaryData(x.IntProperty, BsonBinarySubType.Binary, byteOrder, onErrorBinData, onNullBinData)); var onErrorString = onErrorBase64 == null ? "null" : $"BinData(0, '{onErrorBase64}')"; var onNullString = onNullBase64 == null ? "null" : $"BinData(0, '{onNullBase64}')"; @@ -155,7 +155,7 @@ public void MongoDBFunctions_ConvertToLongDataFromInt_should_work(int id, ByteOr var collection = Fixture.Collection; var queryable = collection.AsQueryable() .Where(x => x.Id == id) - .Select(x => Mql.ToBinData(x.LongProperty, BsonBinarySubType.Binary, byteOrder)); + .Select(x => Mql.ToBsonBinaryData(x.LongProperty, BsonBinarySubType.Binary, byteOrder)); var expectedStages = new[] @@ -191,7 +191,7 @@ public void MongoDBFunctions_ConvertToLongDataFromIntWithOnErrorAndOnNull_should var collection = Fixture.Collection; var queryable = collection.AsQueryable() .Where(x => x.Id == id) - .Select(x => Mql.ToBinData(x.LongProperty, BsonBinarySubType.Binary, byteOrder, onErrorBinData, onNullBinData)); + .Select(x => Mql.ToBsonBinaryData(x.LongProperty, BsonBinarySubType.Binary, byteOrder, onErrorBinData, onNullBinData)); var onErrorString = onErrorBase64 == null ? "null" : $"BinData(0, '{onErrorBase64}')"; var onNullString = onNullBase64 == null ? "null" : $"BinData(0, '{onNullBase64}')"; @@ -216,7 +216,7 @@ public void MongoDBFunctions_ConvertToBinDataFromString_should_work(int id, stri var collection = Fixture.Collection; var queryable = collection.AsQueryable() .Where(x => x.Id == id) - .Select(x => Mql.ToBinData(x.StringProperty, BsonBinarySubType.UuidStandard, "uuid")); + .Select(x => Mql.ToBsonBinaryData(x.StringProperty, BsonBinarySubType.UuidStandard, "uuid")); var expectedStages = new[] @@ -242,7 +242,7 @@ public void MongoDBFunctions_ConvertToBinDataFromStringWithOnErrorAndOnNull_shou var collection = Fixture.Collection; var queryable = collection.AsQueryable() .Where(x => x.Id == id) - .Select(x => Mql.ToBinData(x.StringProperty, BsonBinarySubType.UuidStandard, "uuid", onErrorBinData, onNullBinData)); + .Select(x => Mql.ToBsonBinaryData(x.StringProperty, BsonBinarySubType.UuidStandard, "uuid", onErrorBinData, onNullBinData)); var onErrorString = onErrorBase64 == null ? "null" : $"BinData(0, '{onErrorBase64}')"; var onNullString = onNullBase64 == null ? "null" : $"BinData(0, '{onNullBase64}')"; From c95d8a8f79435dfdb9e163f25b16505eda88502c Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Wed, 26 Mar 2025 10:41:27 +0100 Subject: [PATCH 35/82] Added remark --- src/MongoDB.Driver/Mql.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/MongoDB.Driver/Mql.cs b/src/MongoDB.Driver/Mql.cs index f8ddbd8d950..b8a691793d9 100644 --- a/src/MongoDB.Driver/Mql.cs +++ b/src/MongoDB.Driver/Mql.cs @@ -52,6 +52,7 @@ public static TValue Constant(TValue value, BsonType representaion) /// /// Converts a string to a BsonBinaryData using the $convert aggregation operator. /// + /// The return value could be BsonNull. /// The value. /// The BsonBinaryData subtype of the result value. /// The format string. @@ -64,6 +65,7 @@ public static BsonValue ToBsonBinaryData(string value, BsonBinarySubType subtype /// /// Converts a string to BsonBinaryData using the $convert aggregation operator. /// + /// The return value could be BsonNull. /// The value. /// The BsonBinaryData subtype of the result value. /// The format string. @@ -77,6 +79,7 @@ public static BsonValue ToBsonBinaryData(string value, BsonBinarySubType subtype /// /// Converts an int to BsonBinaryData using the $convert aggregation operator. /// + /// The return value could be BsonNull. /// The value. /// The BsonBinaryData subtype of the result value. /// The byte order of BsonBinaryData. @@ -90,6 +93,7 @@ public static BsonValue ToBsonBinaryData(int? value, BsonBinarySubType subtype, /// Converts an int? to a BsonBinaryData using the $convert aggregation operator. /// /// The value. + /// The return value could be BsonNull. /// The BsonBinaryData subtype of the result value. /// The byte ordering of BsonBinaryData. /// The convert options. @@ -104,6 +108,7 @@ public static BsonValue ToBsonBinaryData(int? value, BsonBinarySubType subtype, /// Converts a long? to a BsonBinaryData using the $convert aggregation operator. /// /// The value. + /// The return value could be BsonNull. /// The BsonBinaryData subtype of the result value. /// The byte ordering of BsonBinaryData. /// The converted value. @@ -115,6 +120,7 @@ public static BsonValue ToBsonBinaryData(long? value, BsonBinarySubType subtype, /// /// Converts a long? to a BsonBinaryData using the $convert aggregation operator. /// + /// The return value could be BsonNull. /// The value. /// The BsonBinaryData subtype of the result value. /// The byte ordering of BsonBinaryData. @@ -129,6 +135,7 @@ public static BsonValue ToBsonBinaryData(long? value, BsonBinarySubType subtype, /// /// Converts a double? to a BsonBinaryData using the $convert aggregation operator. /// + /// The return value could be BsonNull. /// The value. /// The BsonBinaryData subtype of the result value. /// The byte ordering of BsonBinaryData. @@ -141,6 +148,7 @@ public static BsonValue ToBsonBinaryData(double? value, BsonBinarySubType subtyp /// /// Converts a double? to a BsonBinaryData using the $convert aggregation operator. /// + /// The return value could be BsonNull. /// The field. /// The BsonBinaryData subtype of the result value. /// The byte ordering of BsonBinaryData. From 8c4dbf85e22dd451a84ba0f4bd332ecc31bd89e5 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Wed, 26 Mar 2025 10:46:16 +0100 Subject: [PATCH 36/82] Added missing methods --- src/MongoDB.Driver/Mql.cs | 153 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 153 insertions(+) diff --git a/src/MongoDB.Driver/Mql.cs b/src/MongoDB.Driver/Mql.cs index b8a691793d9..beb404b5739 100644 --- a/src/MongoDB.Driver/Mql.cs +++ b/src/MongoDB.Driver/Mql.cs @@ -76,6 +76,34 @@ public static BsonValue ToBsonBinaryData(string value, BsonBinarySubType subtype throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } + /// + /// Converts an int to BsonBinaryData using the $convert aggregation operator. + /// + /// The return value could be BsonNull. + /// The value. + /// The BsonBinaryData subtype of the result value. + /// The byte order of BsonBinaryData. + /// The converted value. + public static BsonValue ToBsonBinaryData(int value, BsonBinarySubType subtype, ByteOrder byteOrder) + { + throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); + } + + /// + /// Converts an int to a BsonBinaryData using the $convert aggregation operator. + /// + /// The value. + /// The return value could be BsonNull. + /// The BsonBinaryData subtype of the result value. + /// The byte ordering of BsonBinaryData. + /// The convert options. + /// The converted value. + /// + public static BsonValue ToBsonBinaryData(int value, BsonBinarySubType subtype, ByteOrder byteOrder, ConvertOptions options) + { + throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); + } + /// /// Converts an int to BsonBinaryData using the $convert aggregation operator. /// @@ -104,6 +132,34 @@ public static BsonValue ToBsonBinaryData(int? value, BsonBinarySubType subtype, throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } + /// + /// Converts a long to a BsonBinaryData using the $convert aggregation operator. + /// + /// The value. + /// The return value could be BsonNull. + /// The BsonBinaryData subtype of the result value. + /// The byte ordering of BsonBinaryData. + /// The converted value. + public static BsonValue ToBsonBinaryData(long value, BsonBinarySubType subtype, ByteOrder byteOrder) + { + throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); + } + + /// + /// Converts a long to a BsonBinaryData using the $convert aggregation operator. + /// + /// The return value could be BsonNull. + /// The value. + /// The BsonBinaryData subtype of the result value. + /// The byte ordering of BsonBinaryData. + /// The convert options. + /// The converted value. + /// + public static BsonValue ToBsonBinaryData(long value, BsonBinarySubType subtype, ByteOrder byteOrder, ConvertOptions options) + { + throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); + } + /// /// Converts a long? to a BsonBinaryData using the $convert aggregation operator. /// @@ -132,6 +188,34 @@ public static BsonValue ToBsonBinaryData(long? value, BsonBinarySubType subtype, throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } + /// + /// Converts a double to a BsonBinaryData using the $convert aggregation operator. + /// + /// The return value could be BsonNull. + /// The value. + /// The BsonBinaryData subtype of the result value. + /// The byte ordering of BsonBinaryData. + /// The converted value. + public static BsonValue ToBsonBinaryData(double value, BsonBinarySubType subtype, ByteOrder byteOrder) + { + throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); + } + + /// + /// Converts a double to a BsonBinaryData using the $convert aggregation operator. + /// + /// The return value could be BsonNull. + /// The field. + /// The BsonBinaryData subtype of the result value. + /// The byte ordering of BsonBinaryData. + /// The convert options. + /// The converted value. + /// + public static BsonValue ToBsonBinaryData(double value, BsonBinarySubType subtype, ByteOrder byteOrder, ConvertOptions options) + { + throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); + } + /// /// Converts a double? to a BsonBinaryData using the $convert aggregation operator. /// @@ -160,6 +244,75 @@ public static BsonValue ToBsonBinaryData(double? value, BsonBinarySubType subtyp throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } + /// + /// Converts a BsonBinaryData to double using the $convert aggregation operator. + /// + /// The value. + /// The byte ordering of BsonBinaryData. + /// The converted value. + public static double ToDouble(BsonBinaryData value, ByteOrder byteOrder) + { + throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); + } + + /// + /// Converts a BsonBinaryData to double using the $convert aggregation operator. + /// + /// The value. + /// The byte ordering of BsonBinaryData. + /// The convert options. + /// The converted value. + public static double ToDouble(BsonBinaryData value, ByteOrder byteOrder, ConvertOptions options) + { + throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); + } + + /// + /// Converts a BsonBinaryData to int using the $convert aggregation operator. + /// + /// The value. + /// The byte ordering of BsonBinaryData. + /// The converted value. + public static int ToInt(BsonBinaryData value, ByteOrder byteOrder) + { + throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); + } + + /// + /// Converts a BsonBinaryData to int using the $convert aggregation operator. + /// + /// The value. + /// The byte ordering of BsonBinaryData. + /// The convert options. + /// The converted value. + public static int ToInt(BsonBinaryData value, ByteOrder byteOrder, ConvertOptions options) + { + throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); + } + + /// + /// Converts a BsonBinaryData to long using the $convert aggregation operator. + /// + /// The value. + /// The byte ordering of BsonBinaryData. + /// The converted value. + public static long ToLong(BsonBinaryData value, ByteOrder byteOrder) + { + throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); + } + + /// + /// Converts a BsonBinaryData to long using the $convert aggregation operator. + /// + /// The value. + /// The byte ordering of BsonBinaryData. + /// The convert options. + /// The converted value. + public static long ToLong(BsonBinaryData value, ByteOrder byteOrder, ConvertOptions options) + { + throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); + } + /// /// Converts a BsonBinaryData to a string using the $convert aggregation operator. /// From 22691b44e3054f97affb81076d0c47f404084c5e Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Wed, 26 Mar 2025 10:58:38 +0100 Subject: [PATCH 37/82] Added missing methods --- .../Reflection/MqlMethod.cs | 75 ++++++++++++++----- 1 file changed, 57 insertions(+), 18 deletions(-) diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/MqlMethod.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/MqlMethod.cs index cd1ed032276..6e7ee8e9e4f 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/MqlMethod.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/MqlMethod.cs @@ -40,7 +40,19 @@ internal static class MqlMethod private static readonly MethodInfo __toBinDataFromIntWithOnErrorAndOnNull; private static readonly MethodInfo __toBinDataFromLong; private static readonly MethodInfo __toBinDataFromLongWithOnErrorAndOnNull; + private static readonly MethodInfo __toBinDataFromNullableDouble; + private static readonly MethodInfo __toBinDataFromNullableDoubleWithOnErrorAndOnNull; + private static readonly MethodInfo __toBinDataFromNullableInt; + private static readonly MethodInfo __toBinDataFromNullableIntWithOnErrorAndOnNull; + private static readonly MethodInfo __toBinDataFromNullableLong; + private static readonly MethodInfo __toBinDataFromNullableLongWithOnErrorAndOnNull; private static readonly MethodInfo __toBinDataFromString; + private static readonly MethodInfo __toDoubleFromBinData; + private static readonly MethodInfo __toDoubleFromBinDataWithOnErrorAndOnNull; + private static readonly MethodInfo __toIntFromBinData; + private static readonly MethodInfo __toIntFromBinDataWithOnErrorAndOnNull; + private static readonly MethodInfo __toLongFromBinData; + private static readonly MethodInfo __toLongFromBinDataWithOnErrorAndOnNull; private static readonly MethodInfo __toBinDataFromStringWithOnErrorAndOnNull; private static readonly MethodInfo __toNullableDoubleFromBinData; private static readonly MethodInfo __toNullableDoubleFromBinDataWithOnErrorAndOnNull; @@ -66,30 +78,45 @@ static MqlMethod() __isNullOrMissing = ReflectionInfo.Method((object field) => Mql.IsNullOrMissing(field)); // Convert methods - __toBinDataFromDouble = ReflectionInfo.Method((double? field, BsonBinarySubType subType, ByteOrder byteOrder) => Mql.ToBsonBinaryData(field, subType, byteOrder)); - __toBinDataFromDoubleWithOnErrorAndOnNull = ReflectionInfo.Method((double? field, BsonBinarySubType subType, ByteOrder byteOrder, BsonBinaryData onError, BsonBinaryData onNull) - => Mql.ToBsonBinaryData(field, subType, byteOrder, onError, onNull)); - __toBinDataFromInt = ReflectionInfo.Method((int? field, BsonBinarySubType subType, ByteOrder byteOrder) => Mql.ToBsonBinaryData(field, subType, byteOrder)); - __toBinDataFromIntWithOnErrorAndOnNull = ReflectionInfo.Method((int? field, BsonBinarySubType subType, ByteOrder byteOrder, BsonBinaryData onError, BsonBinaryData onNull) - => Mql.ToBsonBinaryData(field, subType, byteOrder, onError, onNull)); - __toBinDataFromLong = ReflectionInfo.Method((long? field, BsonBinarySubType subType, ByteOrder byteOrder) => Mql.ToBsonBinaryData(field, subType, byteOrder)); - __toBinDataFromLongWithOnErrorAndOnNull = ReflectionInfo.Method((long? field, BsonBinarySubType subType, ByteOrder byteOrder, BsonBinaryData onError, BsonBinaryData onNull) - => Mql.ToBsonBinaryData(field, subType, byteOrder, onError, onNull)); + + __toBinDataFromDouble = ReflectionInfo.Method((double field, BsonBinarySubType subType, ByteOrder byteOrder) => Mql.ToBsonBinaryData(field, subType, byteOrder)); + __toBinDataFromDoubleWithOnErrorAndOnNull = ReflectionInfo.Method((double field, BsonBinarySubType subType, ByteOrder byteOrder, ConvertOptions options) + => Mql.ToBsonBinaryData(field, subType, byteOrder, options)); + __toBinDataFromInt = ReflectionInfo.Method((int field, BsonBinarySubType subType, ByteOrder byteOrder) => Mql.ToBsonBinaryData(field, subType, byteOrder)); + __toBinDataFromIntWithOnErrorAndOnNull = ReflectionInfo.Method((int field, BsonBinarySubType subType, ByteOrder byteOrder, ConvertOptions options) + => Mql.ToBsonBinaryData(field, subType, byteOrder, options)); + __toBinDataFromLong = ReflectionInfo.Method((long field, BsonBinarySubType subType, ByteOrder byteOrder) => Mql.ToBsonBinaryData(field, subType, byteOrder)); + __toBinDataFromLongWithOnErrorAndOnNull = ReflectionInfo.Method((long field, BsonBinarySubType subType, ByteOrder byteOrder, ConvertOptions options) + => Mql.ToBsonBinaryData(field, subType, byteOrder, options)); + + __toBinDataFromNullableDouble = ReflectionInfo.Method((double? field, BsonBinarySubType subType, ByteOrder byteOrder) => Mql.ToBsonBinaryData(field, subType, byteOrder)); + __toBinDataFromNullableDoubleWithOnErrorAndOnNull = ReflectionInfo.Method((double? field, BsonBinarySubType subType, ByteOrder byteOrder, ConvertOptions options) + => Mql.ToBsonBinaryData(field, subType, byteOrder, options)); + __toBinDataFromNullableInt = ReflectionInfo.Method((int? field, BsonBinarySubType subType, ByteOrder byteOrder) => Mql.ToBsonBinaryData(field, subType, byteOrder)); + __toBinDataFromNullableIntWithOnErrorAndOnNull = ReflectionInfo.Method((int? field, BsonBinarySubType subType, ByteOrder byteOrder, ConvertOptions options) + => Mql.ToBsonBinaryData(field, subType, byteOrder, options)); + __toBinDataFromNullableLong = ReflectionInfo.Method((long? field, BsonBinarySubType subType, ByteOrder byteOrder) => Mql.ToBsonBinaryData(field, subType, byteOrder)); + __toBinDataFromNullableLongWithOnErrorAndOnNull = ReflectionInfo.Method((long? field, BsonBinarySubType subType, ByteOrder byteOrder, ConvertOptions options) + => Mql.ToBsonBinaryData(field, subType, byteOrder, options)); __toBinDataFromString = ReflectionInfo.Method((string field, BsonBinarySubType subType, string format) => Mql.ToBsonBinaryData(field, subType, format)); - __toBinDataFromStringWithOnErrorAndOnNull = ReflectionInfo.Method((string field, BsonBinarySubType subType, string format, BsonBinaryData onError, BsonBinaryData onNull) - => Mql.ToBsonBinaryData(field, subType, format, onError, onNull)); + __toBinDataFromStringWithOnErrorAndOnNull = ReflectionInfo.Method((string field, BsonBinarySubType subType, string format, ConvertOptions options) + => Mql.ToBsonBinaryData(field, subType, format, options)); - __toNullableDoubleFromBinData = ReflectionInfo.Method((BsonBinaryData field, ByteOrder byteOrder) => Mql.ToNullableDouble(field, byteOrder)); - __toNullableDoubleFromBinDataWithOnErrorAndOnNull = ReflectionInfo.Method((BsonBinaryData field, ByteOrder byteOrder, double? onError, double? onNull) => Mql.ToNullableDouble(field, byteOrder, onError, onNull)); + __toDoubleFromBinData = ReflectionInfo.Method((BsonBinaryData field, ByteOrder byteOrder) => Mql.ToDouble(field, byteOrder)); + __toDoubleFromBinDataWithOnErrorAndOnNull = ReflectionInfo.Method((BsonBinaryData field, ByteOrder byteOrder, ConvertOptions options) => Mql.ToDouble(field, byteOrder, options)); + __toIntFromBinData = ReflectionInfo.Method((BsonBinaryData field, ByteOrder byteOrder) => Mql.ToInt(field, byteOrder)); + __toIntFromBinDataWithOnErrorAndOnNull = ReflectionInfo.Method((BsonBinaryData field, ByteOrder byteOrder, ConvertOptions options) => Mql.ToInt(field, byteOrder, options)); + __toLongFromBinData = ReflectionInfo.Method((BsonBinaryData field, ByteOrder byteOrder) => Mql.ToLong(field, byteOrder)); + __toLongFromBinDataWithOnErrorAndOnNull = ReflectionInfo.Method((BsonBinaryData field, ByteOrder byteOrder, ConvertOptions options) => Mql.ToLong(field, byteOrder, options)); + __toNullableDoubleFromBinData = ReflectionInfo.Method((BsonBinaryData field, ByteOrder byteOrder) => Mql.ToNullableDouble(field, byteOrder)); + __toNullableDoubleFromBinDataWithOnErrorAndOnNull = ReflectionInfo.Method((BsonBinaryData field, ByteOrder byteOrder, ConvertOptions options) => Mql.ToNullableDouble(field, byteOrder, options)); __toNullableIntFromBinData = ReflectionInfo.Method((BsonBinaryData field, ByteOrder byteOrder) => Mql.ToNullableInt(field, byteOrder)); - __toNullableIntFromBinDataWithOnErrorAndOnNull = ReflectionInfo.Method((BsonBinaryData field, ByteOrder byteOrder, int? onError, int? onNull) => Mql.ToNullableInt(field, byteOrder, onError, onNull)); - + __toNullableIntFromBinDataWithOnErrorAndOnNull = ReflectionInfo.Method((BsonBinaryData field, ByteOrder byteOrder, ConvertOptions options) => Mql.ToNullableInt(field, byteOrder, options)); __toNullableLongFromBinData = ReflectionInfo.Method((BsonBinaryData field, ByteOrder byteOrder) => Mql.ToNullableLong(field, byteOrder)); - __toNullableLongFromBinDataWithOnErrorAndOnNull = ReflectionInfo.Method((BsonBinaryData field, ByteOrder byteOrder, long? onError, long? onNull) => Mql.ToNullableLong(field, byteOrder, onError, onNull)); - + __toNullableLongFromBinDataWithOnErrorAndOnNull = ReflectionInfo.Method((BsonBinaryData field, ByteOrder byteOrder, ConvertOptions options) => Mql.ToNullableLong(field, byteOrder, options)); __toStringFromBinData = ReflectionInfo.Method((BsonBinaryData field, string format) => Mql.ToString(field, format)); - __toStringFromBinDataWithOnErrorAndOnNull = ReflectionInfo.Method((BsonBinaryData field, string format, string onError, string onNull) => Mql.ToString(field, format, onError, onNull)); + __toStringFromBinDataWithOnErrorAndOnNull = ReflectionInfo.Method((BsonBinaryData field, string format, ConvertOptions options) => Mql.ToString(field, format, options)); } // public properties @@ -109,7 +136,19 @@ static MqlMethod() public static MethodInfo ToBinDataFromIntWithOnErrorAndOnNull => __toBinDataFromIntWithOnErrorAndOnNull; public static MethodInfo ToBinDataFromLong => __toBinDataFromLong; public static MethodInfo ToBinDataFromLongWithOnErrorAndOnNull => __toBinDataFromLongWithOnErrorAndOnNull; + public static MethodInfo ToBinDataFromNullableDouble => __toBinDataFromNullableDouble; + public static MethodInfo ToBinDataFromNullableDoubleWithOnErrorAndOnNull => __toBinDataFromNullableDoubleWithOnErrorAndOnNull; + public static MethodInfo ToBinDataFromNullableInt => __toBinDataFromNullableInt; + public static MethodInfo ToBinDataFromNullableIntWithOnErrorAndOnNull => __toBinDataFromNullableIntWithOnErrorAndOnNull; + public static MethodInfo ToBinDataFromNullableLong => __toBinDataFromNullableLong; + public static MethodInfo ToBinDataFromNullableLongWithOnErrorAndOnNull => __toBinDataFromNullableLongWithOnErrorAndOnNull; public static MethodInfo ToBinDataFromString => __toBinDataFromString; + public static MethodInfo ToDoubleFromBinData => __toDoubleFromBinData; + public static MethodInfo ToDoubleFromBinDataWithOnErrorAndOnNull => __toDoubleFromBinDataWithOnErrorAndOnNull; + public static MethodInfo ToIntFromBinData => __toIntFromBinData; + public static MethodInfo ToIntFromBinDataWithOnErrorAndOnNull => __toIntFromBinDataWithOnErrorAndOnNull; + public static MethodInfo ToLongFromBinData => __toLongFromBinData; + public static MethodInfo ToLongFromBinDataWithOnErrorAndOnNull => __toLongFromBinDataWithOnErrorAndOnNull; public static MethodInfo ToBinDataFromStringWithOnErrorAndOnNull => __toBinDataFromStringWithOnErrorAndOnNull; public static MethodInfo ToNullableDoubleFromBinData => __toNullableDoubleFromBinData; public static MethodInfo ToNullableDoubleFromBinDataWithOnErrorAndOnNull => __toNullableDoubleFromBinDataWithOnErrorAndOnNull; From cfff9b3bceb5652e13aae40d0656b2ff1a81704c Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Wed, 26 Mar 2025 11:07:16 +0100 Subject: [PATCH 38/82] Various corrections --- .../Reflection/MqlMethod.cs | 84 +++++++++---------- ...MethodToAggregationExpressionTranslator.cs | 38 +++++---- 2 files changed, 64 insertions(+), 58 deletions(-) diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/MqlMethod.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/MqlMethod.cs index 6e7ee8e9e4f..7a64f176d1e 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/MqlMethod.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/MqlMethod.cs @@ -35,33 +35,33 @@ internal static class MqlMethod private static readonly MethodInfo __isNullOrMissing; private static readonly MethodInfo __toBinDataFromDouble; - private static readonly MethodInfo __toBinDataFromDoubleWithOnErrorAndOnNull; + private static readonly MethodInfo __toBinDataFromDoubleWithOptions; private static readonly MethodInfo __toBinDataFromInt; - private static readonly MethodInfo __toBinDataFromIntWithOnErrorAndOnNull; + private static readonly MethodInfo __toBinDataFromIntWithOptions; private static readonly MethodInfo __toBinDataFromLong; - private static readonly MethodInfo __toBinDataFromLongWithOnErrorAndOnNull; + private static readonly MethodInfo __toBinDataFromLongWithOptions; private static readonly MethodInfo __toBinDataFromNullableDouble; - private static readonly MethodInfo __toBinDataFromNullableDoubleWithOnErrorAndOnNull; + private static readonly MethodInfo __toBinDataFromNullableDoubleWithOptions; private static readonly MethodInfo __toBinDataFromNullableInt; - private static readonly MethodInfo __toBinDataFromNullableIntWithOnErrorAndOnNull; + private static readonly MethodInfo __toBinDataFromNullableIntWithOptions; private static readonly MethodInfo __toBinDataFromNullableLong; - private static readonly MethodInfo __toBinDataFromNullableLongWithOnErrorAndOnNull; + private static readonly MethodInfo __toBinDataFromNullableLongWithOptions; private static readonly MethodInfo __toBinDataFromString; private static readonly MethodInfo __toDoubleFromBinData; - private static readonly MethodInfo __toDoubleFromBinDataWithOnErrorAndOnNull; + private static readonly MethodInfo __toDoubleFromBinDataWithOptions; private static readonly MethodInfo __toIntFromBinData; - private static readonly MethodInfo __toIntFromBinDataWithOnErrorAndOnNull; + private static readonly MethodInfo __toIntFromBinDataWithOptions; private static readonly MethodInfo __toLongFromBinData; - private static readonly MethodInfo __toLongFromBinDataWithOnErrorAndOnNull; - private static readonly MethodInfo __toBinDataFromStringWithOnErrorAndOnNull; + private static readonly MethodInfo __toLongFromBinDataWithOptions; + private static readonly MethodInfo __toBinDataFromStringWithOptions; private static readonly MethodInfo __toNullableDoubleFromBinData; - private static readonly MethodInfo __toNullableDoubleFromBinDataWithOnErrorAndOnNull; + private static readonly MethodInfo __toNullableDoubleFromBinDataWithOptions; private static readonly MethodInfo __toNullableIntFromBinData; - private static readonly MethodInfo __toNullableIntFromBinDataWithOnErrorAndOnNull; + private static readonly MethodInfo __toNullableIntFromBinDataWithOptions; private static readonly MethodInfo __toNullableLongFromBinData; - private static readonly MethodInfo __toNullableLongFromBinDataWithOnErrorAndOnNull; + private static readonly MethodInfo __toNullableLongFromBinDataWithOptions; private static readonly MethodInfo __toStringFromBinData; - private static readonly MethodInfo __toStringFromBinDataWithOnErrorAndOnNull; + private static readonly MethodInfo __toStringFromBinDataWithOptions; // static constructor static MqlMethod() @@ -80,43 +80,43 @@ static MqlMethod() // Convert methods __toBinDataFromDouble = ReflectionInfo.Method((double field, BsonBinarySubType subType, ByteOrder byteOrder) => Mql.ToBsonBinaryData(field, subType, byteOrder)); - __toBinDataFromDoubleWithOnErrorAndOnNull = ReflectionInfo.Method((double field, BsonBinarySubType subType, ByteOrder byteOrder, ConvertOptions options) + __toBinDataFromDoubleWithOptions = ReflectionInfo.Method((double field, BsonBinarySubType subType, ByteOrder byteOrder, ConvertOptions options) => Mql.ToBsonBinaryData(field, subType, byteOrder, options)); __toBinDataFromInt = ReflectionInfo.Method((int field, BsonBinarySubType subType, ByteOrder byteOrder) => Mql.ToBsonBinaryData(field, subType, byteOrder)); - __toBinDataFromIntWithOnErrorAndOnNull = ReflectionInfo.Method((int field, BsonBinarySubType subType, ByteOrder byteOrder, ConvertOptions options) + __toBinDataFromIntWithOptions = ReflectionInfo.Method((int field, BsonBinarySubType subType, ByteOrder byteOrder, ConvertOptions options) => Mql.ToBsonBinaryData(field, subType, byteOrder, options)); __toBinDataFromLong = ReflectionInfo.Method((long field, BsonBinarySubType subType, ByteOrder byteOrder) => Mql.ToBsonBinaryData(field, subType, byteOrder)); - __toBinDataFromLongWithOnErrorAndOnNull = ReflectionInfo.Method((long field, BsonBinarySubType subType, ByteOrder byteOrder, ConvertOptions options) + __toBinDataFromLongWithOptions = ReflectionInfo.Method((long field, BsonBinarySubType subType, ByteOrder byteOrder, ConvertOptions options) => Mql.ToBsonBinaryData(field, subType, byteOrder, options)); __toBinDataFromNullableDouble = ReflectionInfo.Method((double? field, BsonBinarySubType subType, ByteOrder byteOrder) => Mql.ToBsonBinaryData(field, subType, byteOrder)); - __toBinDataFromNullableDoubleWithOnErrorAndOnNull = ReflectionInfo.Method((double? field, BsonBinarySubType subType, ByteOrder byteOrder, ConvertOptions options) + __toBinDataFromNullableDoubleWithOptions = ReflectionInfo.Method((double? field, BsonBinarySubType subType, ByteOrder byteOrder, ConvertOptions options) => Mql.ToBsonBinaryData(field, subType, byteOrder, options)); __toBinDataFromNullableInt = ReflectionInfo.Method((int? field, BsonBinarySubType subType, ByteOrder byteOrder) => Mql.ToBsonBinaryData(field, subType, byteOrder)); - __toBinDataFromNullableIntWithOnErrorAndOnNull = ReflectionInfo.Method((int? field, BsonBinarySubType subType, ByteOrder byteOrder, ConvertOptions options) + __toBinDataFromNullableIntWithOptions = ReflectionInfo.Method((int? field, BsonBinarySubType subType, ByteOrder byteOrder, ConvertOptions options) => Mql.ToBsonBinaryData(field, subType, byteOrder, options)); __toBinDataFromNullableLong = ReflectionInfo.Method((long? field, BsonBinarySubType subType, ByteOrder byteOrder) => Mql.ToBsonBinaryData(field, subType, byteOrder)); - __toBinDataFromNullableLongWithOnErrorAndOnNull = ReflectionInfo.Method((long? field, BsonBinarySubType subType, ByteOrder byteOrder, ConvertOptions options) + __toBinDataFromNullableLongWithOptions = ReflectionInfo.Method((long? field, BsonBinarySubType subType, ByteOrder byteOrder, ConvertOptions options) => Mql.ToBsonBinaryData(field, subType, byteOrder, options)); __toBinDataFromString = ReflectionInfo.Method((string field, BsonBinarySubType subType, string format) => Mql.ToBsonBinaryData(field, subType, format)); - __toBinDataFromStringWithOnErrorAndOnNull = ReflectionInfo.Method((string field, BsonBinarySubType subType, string format, ConvertOptions options) + __toBinDataFromStringWithOptions = ReflectionInfo.Method((string field, BsonBinarySubType subType, string format, ConvertOptions options) => Mql.ToBsonBinaryData(field, subType, format, options)); __toDoubleFromBinData = ReflectionInfo.Method((BsonBinaryData field, ByteOrder byteOrder) => Mql.ToDouble(field, byteOrder)); - __toDoubleFromBinDataWithOnErrorAndOnNull = ReflectionInfo.Method((BsonBinaryData field, ByteOrder byteOrder, ConvertOptions options) => Mql.ToDouble(field, byteOrder, options)); + __toDoubleFromBinDataWithOptions = ReflectionInfo.Method((BsonBinaryData field, ByteOrder byteOrder, ConvertOptions options) => Mql.ToDouble(field, byteOrder, options)); __toIntFromBinData = ReflectionInfo.Method((BsonBinaryData field, ByteOrder byteOrder) => Mql.ToInt(field, byteOrder)); - __toIntFromBinDataWithOnErrorAndOnNull = ReflectionInfo.Method((BsonBinaryData field, ByteOrder byteOrder, ConvertOptions options) => Mql.ToInt(field, byteOrder, options)); + __toIntFromBinDataWithOptions = ReflectionInfo.Method((BsonBinaryData field, ByteOrder byteOrder, ConvertOptions options) => Mql.ToInt(field, byteOrder, options)); __toLongFromBinData = ReflectionInfo.Method((BsonBinaryData field, ByteOrder byteOrder) => Mql.ToLong(field, byteOrder)); - __toLongFromBinDataWithOnErrorAndOnNull = ReflectionInfo.Method((BsonBinaryData field, ByteOrder byteOrder, ConvertOptions options) => Mql.ToLong(field, byteOrder, options)); + __toLongFromBinDataWithOptions = ReflectionInfo.Method((BsonBinaryData field, ByteOrder byteOrder, ConvertOptions options) => Mql.ToLong(field, byteOrder, options)); __toNullableDoubleFromBinData = ReflectionInfo.Method((BsonBinaryData field, ByteOrder byteOrder) => Mql.ToNullableDouble(field, byteOrder)); - __toNullableDoubleFromBinDataWithOnErrorAndOnNull = ReflectionInfo.Method((BsonBinaryData field, ByteOrder byteOrder, ConvertOptions options) => Mql.ToNullableDouble(field, byteOrder, options)); + __toNullableDoubleFromBinDataWithOptions = ReflectionInfo.Method((BsonBinaryData field, ByteOrder byteOrder, ConvertOptions options) => Mql.ToNullableDouble(field, byteOrder, options)); __toNullableIntFromBinData = ReflectionInfo.Method((BsonBinaryData field, ByteOrder byteOrder) => Mql.ToNullableInt(field, byteOrder)); - __toNullableIntFromBinDataWithOnErrorAndOnNull = ReflectionInfo.Method((BsonBinaryData field, ByteOrder byteOrder, ConvertOptions options) => Mql.ToNullableInt(field, byteOrder, options)); + __toNullableIntFromBinDataWithOptions = ReflectionInfo.Method((BsonBinaryData field, ByteOrder byteOrder, ConvertOptions options) => Mql.ToNullableInt(field, byteOrder, options)); __toNullableLongFromBinData = ReflectionInfo.Method((BsonBinaryData field, ByteOrder byteOrder) => Mql.ToNullableLong(field, byteOrder)); - __toNullableLongFromBinDataWithOnErrorAndOnNull = ReflectionInfo.Method((BsonBinaryData field, ByteOrder byteOrder, ConvertOptions options) => Mql.ToNullableLong(field, byteOrder, options)); + __toNullableLongFromBinDataWithOptions = ReflectionInfo.Method((BsonBinaryData field, ByteOrder byteOrder, ConvertOptions options) => Mql.ToNullableLong(field, byteOrder, options)); __toStringFromBinData = ReflectionInfo.Method((BsonBinaryData field, string format) => Mql.ToString(field, format)); - __toStringFromBinDataWithOnErrorAndOnNull = ReflectionInfo.Method((BsonBinaryData field, string format, ConvertOptions options) => Mql.ToString(field, format, options)); + __toStringFromBinDataWithOptions = ReflectionInfo.Method((BsonBinaryData field, string format, ConvertOptions options) => Mql.ToString(field, format, options)); } // public properties @@ -131,32 +131,32 @@ static MqlMethod() public static MethodInfo IsMissing => __isMissing; public static MethodInfo IsNullOrMissing => __isNullOrMissing; public static MethodInfo ToBinDataFromDouble => __toBinDataFromDouble; - public static MethodInfo ToBinDataFromDoubleWithOnErrorAndOnNull => __toBinDataFromDoubleWithOnErrorAndOnNull; + public static MethodInfo ToBinDataFromDoubleWithOptions => __toBinDataFromDoubleWithOptions; public static MethodInfo ToBinDataFromInt => __toBinDataFromInt; - public static MethodInfo ToBinDataFromIntWithOnErrorAndOnNull => __toBinDataFromIntWithOnErrorAndOnNull; + public static MethodInfo ToBinDataFromIntWithOptions => __toBinDataFromIntWithOptions; public static MethodInfo ToBinDataFromLong => __toBinDataFromLong; - public static MethodInfo ToBinDataFromLongWithOnErrorAndOnNull => __toBinDataFromLongWithOnErrorAndOnNull; + public static MethodInfo ToBinDataFromLongWithOptions => __toBinDataFromLongWithOptions; public static MethodInfo ToBinDataFromNullableDouble => __toBinDataFromNullableDouble; - public static MethodInfo ToBinDataFromNullableDoubleWithOnErrorAndOnNull => __toBinDataFromNullableDoubleWithOnErrorAndOnNull; + public static MethodInfo ToBinDataFromNullableDoubleWithOptions => __toBinDataFromNullableDoubleWithOptions; public static MethodInfo ToBinDataFromNullableInt => __toBinDataFromNullableInt; - public static MethodInfo ToBinDataFromNullableIntWithOnErrorAndOnNull => __toBinDataFromNullableIntWithOnErrorAndOnNull; + public static MethodInfo ToBinDataFromNullableIntWithOptions => __toBinDataFromNullableIntWithOptions; public static MethodInfo ToBinDataFromNullableLong => __toBinDataFromNullableLong; - public static MethodInfo ToBinDataFromNullableLongWithOnErrorAndOnNull => __toBinDataFromNullableLongWithOnErrorAndOnNull; + public static MethodInfo ToBinDataFromNullableLongWithOptions => __toBinDataFromNullableLongWithOptions; public static MethodInfo ToBinDataFromString => __toBinDataFromString; public static MethodInfo ToDoubleFromBinData => __toDoubleFromBinData; - public static MethodInfo ToDoubleFromBinDataWithOnErrorAndOnNull => __toDoubleFromBinDataWithOnErrorAndOnNull; + public static MethodInfo ToDoubleFromBinDataWithOptions => __toDoubleFromBinDataWithOptions; public static MethodInfo ToIntFromBinData => __toIntFromBinData; - public static MethodInfo ToIntFromBinDataWithOnErrorAndOnNull => __toIntFromBinDataWithOnErrorAndOnNull; + public static MethodInfo ToIntFromBinDataWithOptions => __toIntFromBinDataWithOptions; public static MethodInfo ToLongFromBinData => __toLongFromBinData; - public static MethodInfo ToLongFromBinDataWithOnErrorAndOnNull => __toLongFromBinDataWithOnErrorAndOnNull; - public static MethodInfo ToBinDataFromStringWithOnErrorAndOnNull => __toBinDataFromStringWithOnErrorAndOnNull; + public static MethodInfo ToLongFromBinDataWithOptions => __toLongFromBinDataWithOptions; + public static MethodInfo ToBinDataFromStringWithOptions => __toBinDataFromStringWithOptions; public static MethodInfo ToNullableDoubleFromBinData => __toNullableDoubleFromBinData; - public static MethodInfo ToNullableDoubleFromBinDataWithOnErrorAndOnNull => __toNullableDoubleFromBinDataWithOnErrorAndOnNull; + public static MethodInfo ToNullableDoubleFromBinDataWithOptions => __toNullableDoubleFromBinDataWithOptions; public static MethodInfo ToNullableIntFromBinData => __toNullableIntFromBinData; - public static MethodInfo ToNullableIntFromBinDataWithOnErrorAndOnNull => __toNullableIntFromBinDataWithOnErrorAndOnNull; + public static MethodInfo ToNullableIntFromBinDataWithOptions => __toNullableIntFromBinDataWithOptions; public static MethodInfo ToNullableLongFromBinData => __toNullableLongFromBinData; - public static MethodInfo ToNullableLongFromBinDataWithOnErrorAndOnNull => __toNullableLongFromBinDataWithOnErrorAndOnNull; + public static MethodInfo ToNullableLongFromBinDataWithOptions => __toNullableLongFromBinDataWithOptions; public static MethodInfo ToStringFromBinData => __toStringFromBinData; - public static MethodInfo ToStringFromBinDataWithOnErrorAndOnNull => __toStringFromBinDataWithOnErrorAndOnNull; + public static MethodInfo ToStringFromBinDataWithOptions => __toStringFromBinDataWithOptions; } } diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs index d980b04cd58..54764d22405 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs @@ -14,26 +14,32 @@ namespace MongoDB.Driver.Linq.Linq3Implementation.Translators.ExpressionToAggreg { internal class ConvertMethodToAggregationExpressionTranslator { - private static readonly List<(MethodInfo[] Methods, IBsonSerializer Serializer, BsonType Type, int? FormatIndex, int? SubTypeIndex, int? ByteOrderIndex, int? OnErrorIndex, int? OnNullIndex)> _methodMappings = + private static readonly List<(MethodInfo[] Methods, IBsonSerializer Serializer, BsonType Type, int? FormatIndex, int? SubTypeIndex, int? ByteOrderIndex, int? optionsIndex)> _methodMappings = [ - ([MqlMethod.ToBinDataFromString], BsonBinaryDataSerializer.Instance, BsonType.Binary, 2, 1, null, null, null), - ([MqlMethod.ToBinDataFromInt, MqlMethod.ToBinDataFromLong, MqlMethod.ToBinDataFromDouble], - BsonBinaryDataSerializer.Instance, BsonType.Binary, null, 1, 2, null, null), - ([MqlMethod.ToBinDataFromStringWithOnErrorAndOnNull], BsonBinaryDataSerializer.Instance, BsonType.Binary, 2, 1, null, 3, 4), - ([MqlMethod.ToBinDataFromIntWithOnErrorAndOnNull, MqlMethod.ToBinDataFromLongWithOnErrorAndOnNull, MqlMethod.ToBinDataFromDoubleWithOnErrorAndOnNull], - BsonBinaryDataSerializer.Instance, BsonType.Binary, null, 1, 2, 3, 4), - ([MqlMethod.ToStringFromBinData], StringSerializer.Instance, BsonType.String, 1, null, null, null, null), - ([MqlMethod.ToStringFromBinDataWithOnErrorAndOnNull], StringSerializer.Instance, BsonType.String, 1, null, null, 2, 3), - ([MqlMethod.ToNullableIntFromBinData], new NullableSerializer(Int32Serializer.Instance), BsonType.Int32, null, null, 1, null, null), - ([MqlMethod.ToNullableIntFromBinDataWithOnErrorAndOnNull], new NullableSerializer(Int32Serializer.Instance), BsonType.Int32, null, null, 1, 2, 3), - ([MqlMethod.ToNullableLongFromBinData], new NullableSerializer(Int64Serializer.Instance), BsonType.Int64, null, null, 1, null, null), - ([MqlMethod.ToNullableLongFromBinDataWithOnErrorAndOnNull], new NullableSerializer(Int64Serializer.Instance), BsonType.Int64, null, null, 1, 2, 3), - ([MqlMethod.ToNullableDoubleFromBinData], new NullableSerializer(DoubleSerializer.Instance), BsonType.Double, null, null, 1, null, null), - ([MqlMethod.ToNullableDoubleFromBinDataWithOnErrorAndOnNull], new NullableSerializer(DoubleSerializer.Instance), BsonType.Double, null, null, 1, 2, 3) + ([MqlMethod.ToBinDataFromString], BsonValueSerializer.Instance, BsonType.Binary, 2, 1, null, null), + ([MqlMethod.ToBinDataFromInt, MqlMethod.ToBinDataFromLong, MqlMethod.ToBinDataFromDouble], BsonValueSerializer.Instance, BsonType.Binary, null, 1, 2, null), + ([MqlMethod.ToBinDataFromStringWithOptions], BsonValueSerializer.Instance, BsonType.Binary, 2, 1, null, 3), + ([MqlMethod.ToBinDataFromIntWithOptions, MqlMethod.ToBinDataFromLongWithOptions, MqlMethod.ToBinDataFromDoubleWithOptions], BsonValueSerializer.Instance, BsonType.Binary, null, 1, 2, 3), + + ([MqlMethod.ToDoubleFromBinData], DoubleSerializer.Instance, BsonType.Double, null, null, 1, null), + ([MqlMethod.ToDoubleFromBinDataWithOptions], DoubleSerializer.Instance, BsonType.Double, null, null, 1, 2), + ([MqlMethod.ToIntFromBinData], Int32Serializer.Instance, BsonType.Int32, null, null, 1, null), + ([MqlMethod.ToIntFromBinDataWithOptions], Int32Serializer.Instance, BsonType.Int32, null, null, 1, 2), + ([MqlMethod.ToLongFromBinData], Int64Serializer.Instance, BsonType.Int64, null, null, 1, null), + ([MqlMethod.ToLongFromBinDataWithOptions], Int64Serializer.Instance, BsonType.Int64, null, null, 1, 2), + + ([MqlMethod.ToNullableDoubleFromBinData], new NullableSerializer(DoubleSerializer.Instance), BsonType.Double, null, null, 1, null), + ([MqlMethod.ToNullableDoubleFromBinDataWithOptions], new NullableSerializer(DoubleSerializer.Instance), BsonType.Double, null, null, 1, 2), + ([MqlMethod.ToNullableIntFromBinData], new NullableSerializer(Int32Serializer.Instance), BsonType.Int32, null, null, 1, null), + ([MqlMethod.ToNullableIntFromBinDataWithOptions], new NullableSerializer(Int32Serializer.Instance), BsonType.Int32, null, null, 1, 2), + ([MqlMethod.ToNullableLongFromBinData], new NullableSerializer(Int64Serializer.Instance), BsonType.Int64, null, null, 1, null), + ([MqlMethod.ToNullableLongFromBinDataWithOptions], new NullableSerializer(Int64Serializer.Instance), BsonType.Int64, null, null, 1, 2), + ([MqlMethod.ToStringFromBinData], StringSerializer.Instance, BsonType.String, 1, null, null, null), + ([MqlMethod.ToStringFromBinDataWithOptions], StringSerializer.Instance, BsonType.String, 1, null, null, 2), ]; private static readonly List ToStringMethods = - [MqlMethod.ToStringFromBinData, MqlMethod.ToStringFromBinDataWithOnErrorAndOnNull]; + [MqlMethod.ToStringFromBinData, MqlMethod.ToStringFromBinDataWithOptions]; public static bool IsConvertToStringMethod(MethodInfo method) { From a341cfec83a270e8c241fb952ed94a78eaa3d760 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Wed, 26 Mar 2025 13:48:56 +0100 Subject: [PATCH 39/82] Various corrections --- .../Ast/Expressions/AstConvertExpression.cs | 24 +- .../Ast/Expressions/AstExpression.cs | 7 +- .../Ast/Visitors/AstNodeVisitor.cs | 2 +- ...essionToAggregationExpressionTranslator.cs | 5 +- ...MethodToAggregationExpressionTranslator.cs | 35 +- src/MongoDB.Driver/Mql.cs | 30 +- .../Ast/Expressions/AstExpressionTests.cs | 38 +- ...dToAggregationExpressionTranslatorTests.cs | 462 +++++++++--------- 8 files changed, 327 insertions(+), 276 deletions(-) diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstConvertExpression.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstConvertExpression.cs index 14b73f5ea6e..abfa937939b 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstConvertExpression.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstConvertExpression.cs @@ -24,8 +24,7 @@ internal sealed class AstConvertExpression : AstExpression private readonly ByteOrder? _byteOrder; private readonly AstExpression _input; private readonly string _format; - private readonly AstExpression _onError; - private readonly AstExpression _onNull; + private readonly ConvertOptions _options; private readonly BsonBinarySubType? _subType; private readonly AstExpression _to; @@ -35,24 +34,21 @@ public AstConvertExpression( BsonBinarySubType? subType = null, ByteOrder? byteOrder = null, string format = null, - AstExpression onError = null, - AstExpression onNull = null) + ConvertOptions options = null) { _input = Ensure.IsNotNull(input, nameof(input)); _to = Ensure.IsNotNull(to, nameof(to)); _subType = subType; _byteOrder = byteOrder; _format = format; - _onError = onError; - _onNull = onNull; + _options = options; } public ByteOrder? ByteOrder => _byteOrder; public AstExpression Input => _input; public string Format => _format; public override AstNodeType NodeType => AstNodeType.ConvertExpression; - public AstExpression OnError => _onError; - public AstExpression OnNull => _onNull; + public ConvertOptions Options => _options; public BsonBinarySubType? SubType => _subType; public AstExpression To => _to; @@ -75,8 +71,8 @@ public override BsonValue Render() {"subtype", (int)_subType!.Value}, }, _subType != null }, - { "onError", () => _onError.Render(), _onError != null }, - { "onNull", () => _onNull.Render(), _onNull != null }, + { "onError", () => _options.RenderOnError(), _options?.OnErrorWasSet == true }, + { "onNull", () => _options.RenderOnNull(), _options?.OnNullWasSet == true }, { "format", () => _format, _format != null}, { "byteOrder", () => _byteOrder!.Value.Render(), _byteOrder != null} } @@ -86,16 +82,14 @@ public override BsonValue Render() public AstConvertExpression Update( AstExpression input, - AstExpression to, - AstExpression onError, - AstExpression onNull) + AstExpression to) { - if (input == _input && to == _to && onError == _onError && onNull == _onNull) + if (input == _input && to == _to) { return this; } - return new AstConvertExpression(input, to, _subType, _byteOrder, _format, onError, onNull); + return new AstConvertExpression(input, to, _subType, _byteOrder, _format, _options); } } } diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstExpression.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstExpression.cs index 8de612f95f1..5de49b28f0e 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstExpression.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstExpression.cs @@ -261,13 +261,12 @@ public static AstExpression Convert( BsonBinarySubType? subType = null, ByteOrder? byteOrder = null, string format = null, - AstExpression onError = null, - AstExpression onNull = null) + ConvertOptions options = null) { Ensure.IsNotNull(input, nameof(input)); Ensure.IsNotNull(to, nameof(to)); - if (onError == null && onNull == null && subType == null && format == null && byteOrder == null && + if (options == null && subType == null && format == null && byteOrder == null && to is AstConstantExpression toConstantExpression && (toConstantExpression.Value as BsonString)?.Value is { } toValue) { @@ -290,7 +289,7 @@ to is AstConstantExpression toConstantExpression && } } - return new AstConvertExpression(input, to, subType, byteOrder, format, onError, onNull); + return new AstConvertExpression(input, to, subType, byteOrder, format, options); } public static AstExpression DateAdd( diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Visitors/AstNodeVisitor.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Visitors/AstNodeVisitor.cs index 1222d0060e9..4b807f759ba 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Visitors/AstNodeVisitor.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Visitors/AstNodeVisitor.cs @@ -226,7 +226,7 @@ public virtual AstNode VisitConstantExpression(AstConstantExpression node) public virtual AstNode VisitConvertExpression(AstConvertExpression node) { - return node.Update(VisitAndConvert(node.Input), VisitAndConvert(node.To), VisitAndConvert(node.OnError), VisitAndConvert(node.OnNull)); + return node.Update(VisitAndConvert(node.Input), VisitAndConvert(node.To)); } public virtual AstNode VisitCountStage(AstCountStage node) diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodCallExpressionToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodCallExpressionToAggregationExpressionTranslator.cs index ca1723fe72e..4cdc67aac94 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodCallExpressionToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodCallExpressionToAggregationExpressionTranslator.cs @@ -138,7 +138,10 @@ public static TranslatedExpression Translate(TranslationContext context, MethodC case "LongCount": return CountMethodToAggregationExpressionTranslator.Translate(context, expression); - case "ToBinData": + case "ToBsonBinaryData": + case "ToDouble": + case "ToInt": + case "TLong": case "ToNullableDouble": case "ToNullableInt": case "ToNullableLong": diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs index 54764d22405..9d3ae33a982 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs @@ -14,10 +14,11 @@ namespace MongoDB.Driver.Linq.Linq3Implementation.Translators.ExpressionToAggreg { internal class ConvertMethodToAggregationExpressionTranslator { - private static readonly List<(MethodInfo[] Methods, IBsonSerializer Serializer, BsonType Type, int? FormatIndex, int? SubTypeIndex, int? ByteOrderIndex, int? optionsIndex)> _methodMappings = + private static readonly List<(MethodInfo[] Methods, IBsonSerializer Serializer, BsonType Type, int? FormatIndex, int? SubTypeIndex, int? ByteOrderIndex, int? OptionsIndex)> _methodMappings = [ ([MqlMethod.ToBinDataFromString], BsonValueSerializer.Instance, BsonType.Binary, 2, 1, null, null), - ([MqlMethod.ToBinDataFromInt, MqlMethod.ToBinDataFromLong, MqlMethod.ToBinDataFromDouble], BsonValueSerializer.Instance, BsonType.Binary, null, 1, 2, null), + ([MqlMethod.ToBinDataFromInt, MqlMethod.ToBinDataFromLong, MqlMethod.ToBinDataFromDouble, MqlMethod.ToBinDataFromNullableInt, MqlMethod.ToBinDataFromNullableLong, MqlMethod.ToBinDataFromNullableDouble], + BsonValueSerializer.Instance, BsonType.Binary, null, 1, 2, null), ([MqlMethod.ToBinDataFromStringWithOptions], BsonValueSerializer.Instance, BsonType.Binary, 2, 1, null, 3), ([MqlMethod.ToBinDataFromIntWithOptions, MqlMethod.ToBinDataFromLongWithOptions, MqlMethod.ToBinDataFromDoubleWithOptions], BsonValueSerializer.Instance, BsonType.Binary, null, 1, 2, 3), @@ -41,6 +42,9 @@ internal class ConvertMethodToAggregationExpressionTranslator private static readonly List ToStringMethods = [MqlMethod.ToStringFromBinData, MqlMethod.ToStringFromBinDataWithOptions]; + private static readonly List ToNumericalMethodsWithOptions = + [MqlMethod.ToIntFromBinDataWithOptions, MqlMethod.ToLongFromBinDataWithOptions, MqlMethod.ToDoubleFromBinDataWithOptions]; + public static bool IsConvertToStringMethod(MethodInfo method) { return ToStringMethods.Contains(method); @@ -58,10 +62,9 @@ public static TranslatedExpression Translate(TranslationContext context, MethodC ByteOrder? byteOrder = null; BsonBinarySubType? subType = null; string format = null; + ConvertOptions options = null; var fieldAst = ExpressionToAggregationExpressionTranslator.Translate(context, arguments[0]).Ast; - var onErrorAst = mapping.OnErrorIndex.HasValue ? ExpressionToAggregationExpressionTranslator.Translate(context, arguments[mapping.OnErrorIndex.Value]).Ast : null; - var onNullAst = mapping.OnNullIndex.HasValue ? ExpressionToAggregationExpressionTranslator.Translate(context, arguments[mapping.OnNullIndex.Value]).Ast : null; if (mapping.ByteOrderIndex.HasValue) { @@ -99,7 +102,29 @@ public static TranslatedExpression Translate(TranslationContext context, MethodC } } - var ast = AstExpression.Convert(fieldAst, mapping.Type.Render(), subType: subType, byteOrder: byteOrder, format: format, onError: onErrorAst, onNull: onNullAst); + if (mapping.OptionsIndex.HasValue) + { + if (arguments[mapping.OptionsIndex.Value] is ConstantExpression co) + { + options = (ConvertOptions)co.Value!; + + if (options == null) + { + throw new InvalidOperationException("The 'options' argument cannot be null"); + } + + if (ToNumericalMethodsWithOptions.Contains(method) && !options.OnNullWasSet) + { + throw new InvalidOperationException("When converting to a non-nullable type, you need to set 'onNull'"); + } + } + else + { + throw new InvalidOperationException("The 'options' argument must be a constant expression"); + } + } + + var ast = AstExpression.Convert(fieldAst, mapping.Type.Render(), subType: subType, byteOrder: byteOrder, format: format, options: options); return new TranslatedExpression(expression, ast, mapping.Serializer); } } diff --git a/src/MongoDB.Driver/Mql.cs b/src/MongoDB.Driver/Mql.cs index beb404b5739..99820f7d9f5 100644 --- a/src/MongoDB.Driver/Mql.cs +++ b/src/MongoDB.Driver/Mql.cs @@ -525,12 +525,26 @@ public enum ByteOrder LittleEndian, } + /// + /// + /// + public abstract class ConvertOptions + { + internal abstract bool OnErrorWasSet { get; } + + internal abstract bool OnNullWasSet { get; } + + internal abstract BsonValue RenderOnError(); + + internal abstract BsonValue RenderOnNull(); + } + //TODO Add docs /// /// /// /// - public class ConvertOptions + public class ConvertOptions : ConvertOptions { private TResult _onError; private bool _onErrorWasSet; @@ -563,7 +577,17 @@ public TResult OnNull } } - internal bool OnErrorWasSet => _onErrorWasSet; - internal bool OnNullWasSet => _onNullWasSet; + internal override bool OnErrorWasSet => _onErrorWasSet; + internal override bool OnNullWasSet => _onNullWasSet; + + internal override BsonValue RenderOnError() //TODO Maybe I don't need to do this here... + { + return BsonValue.Create(_onError); + } + + internal override BsonValue RenderOnNull() + { + return BsonValue.Create(_onNull); + } } } diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Ast/Expressions/AstExpressionTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Ast/Expressions/AstExpressionTests.cs index 0f313e27f50..067b943e634 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Ast/Expressions/AstExpressionTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Ast/Expressions/AstExpressionTests.cs @@ -36,7 +36,7 @@ public void Convert_with_to_constant_should_return_short_form_when_possible(stri var input = AstExpression.Constant(BsonNull.Value); var to = AstExpression.Constant(toValue); - var result = AstExpression.Convert(input, to, onError: null, onNull: null); + var result = AstExpression.Convert(input, to); var unaryExpression = result.Should().BeOfType().Subject; unaryExpression.Operator.Should().Be((AstUnaryOperator)expectedOperator); @@ -50,13 +50,12 @@ public void Convert_with_to_constant_should_return_long_form_when_necessary(stri var input = AstExpression.Constant(BsonNull.Value); var to = AstExpression.Constant(toValue); - var result = AstExpression.Convert(input, to, onError: null, onNull: null); + var result = AstExpression.Convert(input, to); var convertExpression = result.Should().BeOfType().Subject; convertExpression.Input.Should().BeSameAs(input); convertExpression.To.Should().BeSameAs(to); - convertExpression.OnError.Should().BeNull(); - convertExpression.OnNull.Should().BeNull(); + convertExpression.Options.Should().BeNull(); } [Theory] @@ -73,13 +72,12 @@ public void Convert_with_to_expression_should_return_long_form(string toValue) var input = AstExpression.Constant(BsonNull.Value); var to = AstExpression.FieldPath("$To"); - var result = AstExpression.Convert(input, to, onError: null, onNull: null); + var result = AstExpression.Convert(input, to); var convertExpression = result.Should().BeOfType().Subject; convertExpression.Input.Should().BeSameAs(input); convertExpression.To.Should().BeSameAs(to); - convertExpression.OnError.Should().BeNull(); - convertExpression.OnNull.Should().BeNull(); + convertExpression.Options.Should().BeNull(); } [Theory] @@ -95,15 +93,20 @@ public void Convert_with_on_error_should_return_long_form(string toValue) { var input = AstExpression.Constant(BsonNull.Value); var to = AstExpression.Constant(toValue); - var onError = AstExpression.Constant(BsonNull.Value); + var options = new ConvertOptions { OnError = BsonNull.Value }; - var result = AstExpression.Convert(input, to, onError: onError, onNull: null); + var result = AstExpression.Convert(input, to, options: options); var convertExpression = result.Should().BeOfType().Subject; convertExpression.Input.Should().BeSameAs(input); convertExpression.To.Should().BeSameAs(to); - convertExpression.OnError.Should().BeSameAs(onError); - convertExpression.OnNull.Should().BeNull(); + + convertExpression.Options.Should().BeOfType>(); + var retrievedOptions = (ConvertOptions)convertExpression.Options; + retrievedOptions.OnErrorWasSet.Should().BeTrue(); + retrievedOptions.OnError.Should().BeSameAs(options.OnError); + retrievedOptions.OnNullWasSet.Should().BeFalse(); + retrievedOptions.OnNull.Should().BeSameAs(null); } [Theory] @@ -119,15 +122,20 @@ public void Convert_with_on_null_should_return_long_form(string toValue) { var input = AstExpression.Constant(BsonNull.Value); var to = AstExpression.Constant(toValue); - var onNull = AstExpression.Constant(BsonNull.Value); + var options = new ConvertOptions { OnNull = BsonNull.Value }; - var result = AstExpression.Convert(input, to, onError: null, onNull: onNull); + var result = AstExpression.Convert(input, to, options: options); var convertExpression = result.Should().BeOfType().Subject; convertExpression.Input.Should().BeSameAs(input); convertExpression.To.Should().BeSameAs(to); - convertExpression.OnError.Should().BeNull(); - convertExpression.OnNull.Should().BeSameAs(onNull); + + convertExpression.Options.Should().BeOfType>(); + var retrievedOptions = (ConvertOptions)convertExpression.Options; + retrievedOptions.OnErrorWasSet.Should().BeFalse(); + retrievedOptions.OnError.Should().BeSameAs(null); + retrievedOptions.OnNullWasSet.Should().BeTrue(); + retrievedOptions.OnNull.Should().BeSameAs(options.OnNull); } [Fact] diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs index 19f9d8f6527..29f3f63c31e 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs @@ -15,10 +15,8 @@ using System; using System.Collections.Generic; -using System.Globalization; using System.Linq; using MongoDB.Bson; -using MongoDB.Bson.Serialization.Attributes; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.TestHelpers.XunitExtensions; using MongoDB.Driver.TestHelpers; @@ -40,7 +38,7 @@ public ConvertMethodToAggregationExpressionTranslatorTests(ClassFixture fixture) [InlineData(1, ByteOrder.BigEndian, null, "FormatException")] [InlineData(3, ByteOrder.LittleEndian,"AAAAAAAA4L8=", null)] [InlineData(5, ByteOrder.BigEndian, "wAQAAAAAAAA=", null )] - public void MongoDBFunctions_ConvertToBinDataFromDouble_should_work(int id, ByteOrder byteOrder, string expectedBase64, string expectedException) + public void MongoDBFunctions_ConvertToBsonBinaryDataFromDouble_should_work(int id, ByteOrder byteOrder, string expectedBase64, string expectedException) { RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); @@ -60,41 +58,41 @@ public void MongoDBFunctions_ConvertToBinDataFromDouble_should_work(int id, Byte AssertOutcome(collection, queryable, expectedStages, expectedResult, expectedException); } - [Theory] - [InlineData(0, ByteOrder.LittleEndian, "AAAAAAAABMA=", "Ag==", "AAAAAAAABMA=")] - [InlineData(10, ByteOrder.LittleEndian, "Ag==", "Ag==", "AAAAAAAABMA=")] - [InlineData(0, ByteOrder.BigEndian, "AAAAAAAABMA=", "Ag==", "AAAAAAAABMA=")] - [InlineData(10, ByteOrder.BigEndian, "Ag==", "Ag==", "AAAAAAAABMA=")] - public void MongoDBFunctions_ConvertToBinDataFromDoubleWithOnErrorAndOnNull_should_work(int id, ByteOrder byteOrder, string expectedBase64, string onErrorBase64, string onNullBase64) - { - RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); - - var onErrorBinData = onErrorBase64 == null ? null : new BsonBinaryData(Convert.FromBase64String(onErrorBase64)); - var onNullBinData = onNullBase64 == null ? null : new BsonBinaryData(Convert.FromBase64String(onNullBase64)); - - var collection = Fixture.Collection; - var queryable = collection.AsQueryable() - .Where(x => x.Id == id) - .Select(x => Mql.ToBsonBinaryData(x.DoubleProperty, BsonBinarySubType.Binary, byteOrder, onErrorBinData, onNullBinData)); - - var onErrorString = onErrorBase64 == null ? "null" : $"BinData(0, '{onErrorBase64}')"; - var onNullString = onNullBase64 == null ? "null" : $"BinData(0, '{onNullBase64}')"; - var expectedStages = - new[] - { - $"{{ $match : {{ _id : {id} }} }}", - $"{{ $project: {{ _v : {{ $convert : {{ input : '$DoubleProperty', to : {{ type: 'binData', subtype: 0 }}, onError: {onErrorString}, onNull: {onNullString}, {ByteOrderToString(byteOrder)}}} }}, _id : 0 }} }}", - }; - - var expectedResult = expectedBase64 is null? null : new BsonBinaryData(Convert.FromBase64String(expectedBase64)); - AssertOutcome(collection, queryable, expectedStages, expectedResult); - } + // [Theory] + // [InlineData(0, ByteOrder.LittleEndian, "AAAAAAAABMA=", "Ag==", "AAAAAAAABMA=")] + // [InlineData(10, ByteOrder.LittleEndian, "Ag==", "Ag==", "AAAAAAAABMA=")] + // [InlineData(0, ByteOrder.BigEndian, "AAAAAAAABMA=", "Ag==", "AAAAAAAABMA=")] + // [InlineData(10, ByteOrder.BigEndian, "Ag==", "Ag==", "AAAAAAAABMA=")] + // public void MongoDBFunctions_ConvertToBsonBinaryDataFromDoubleWithOnErrorAndOnNull_should_work(int id, ByteOrder byteOrder, string expectedBase64, string onErrorBase64, string onNullBase64) + // { + // RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); + // + // var onErrorBinData = onErrorBase64 == null ? null : new BsonBinaryData(Convert.FromBase64String(onErrorBase64)); + // var onNullBinData = onNullBase64 == null ? null : new BsonBinaryData(Convert.FromBase64String(onNullBase64)); + // + // var collection = Fixture.Collection; + // var queryable = collection.AsQueryable() + // .Where(x => x.Id == id) + // .Select(x => Mql.ToBsonBinaryData(x.DoubleProperty, BsonBinarySubType.Binary, byteOrder, onErrorBinData, onNullBinData)); + // + // var onErrorString = onErrorBase64 == null ? "null" : $"BinData(0, '{onErrorBase64}')"; + // var onNullString = onNullBase64 == null ? "null" : $"BinData(0, '{onNullBase64}')"; + // var expectedStages = + // new[] + // { + // $"{{ $match : {{ _id : {id} }} }}", + // $"{{ $project: {{ _v : {{ $convert : {{ input : '$DoubleProperty', to : {{ type: 'binData', subtype: 0 }}, onError: {onErrorString}, onNull: {onNullString}, {ByteOrderToString(byteOrder)}}} }}, _id : 0 }} }}", + // }; + // + // var expectedResult = expectedBase64 is null? null : new BsonBinaryData(Convert.FromBase64String(expectedBase64)); + // AssertOutcome(collection, queryable, expectedStages, expectedResult); + // } [Theory] [InlineData(1, ByteOrder.BigEndian, null, "FormatException")] [InlineData(4, ByteOrder.LittleEndian,"ogIAAA==", null)] [InlineData(6, ByteOrder.BigEndian, "AAAAKg==", null )] - public void MongoDBFunctions_ConvertToBinDataFromInt_should_work(int id, ByteOrder byteOrder, string expectedBase64, string expectedException) + public void MongoDBFunctions_ConvertToBsonBinaryDataFromInt_should_work(int id, ByteOrder byteOrder, string expectedBase64, string expectedException) { RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); @@ -114,41 +112,41 @@ public void MongoDBFunctions_ConvertToBinDataFromInt_should_work(int id, ByteOrd AssertOutcome(collection, queryable, expectedStages, expectedResult, expectedException); } - [Theory] - [InlineData(0, ByteOrder.LittleEndian, "AAAAAAAABMA=", "Ag==", "AAAAAAAABMA=")] - [InlineData(10, ByteOrder.LittleEndian, "Ag==", "Ag==", "AAAAAAAABMA=")] - [InlineData(0, ByteOrder.BigEndian, "AAAAAAAABMA=", "Ag==", "AAAAAAAABMA=")] - [InlineData(10, ByteOrder.BigEndian, "Ag==", "Ag==", "AAAAAAAABMA=")] - public void MongoDBFunctions_ConvertToBinDataFromIntWithOnErrorAndOnNull_should_work(int id, ByteOrder byteOrder, string expectedBase64, string onErrorBase64, string onNullBase64) - { - RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); - - var onErrorBinData = onErrorBase64 == null ? null : new BsonBinaryData(Convert.FromBase64String(onErrorBase64)); - var onNullBinData = onNullBase64 == null ? null : new BsonBinaryData(Convert.FromBase64String(onNullBase64)); - - var collection = Fixture.Collection; - var queryable = collection.AsQueryable() - .Where(x => x.Id == id) - .Select(x => Mql.ToBsonBinaryData(x.IntProperty, BsonBinarySubType.Binary, byteOrder, onErrorBinData, onNullBinData)); - - var onErrorString = onErrorBase64 == null ? "null" : $"BinData(0, '{onErrorBase64}')"; - var onNullString = onNullBase64 == null ? "null" : $"BinData(0, '{onNullBase64}')"; - var expectedStages = - new[] - { - $"{{ $match : {{ _id : {id} }} }}", - $"{{ $project: {{ _v : {{ $convert : {{ input : '$IntProperty', to : {{ type: 'binData', subtype: 0 }}, onError: {onErrorString}, onNull: {onNullString}, {ByteOrderToString(byteOrder)}}} }}, _id : 0 }} }}", - }; - - var expectedResult = expectedBase64 is null? null : new BsonBinaryData(Convert.FromBase64String(expectedBase64)); - AssertOutcome(collection, queryable, expectedStages, expectedResult); - } + // [Theory] + // [InlineData(0, ByteOrder.LittleEndian, "AAAAAAAABMA=", "Ag==", "AAAAAAAABMA=")] + // [InlineData(10, ByteOrder.LittleEndian, "Ag==", "Ag==", "AAAAAAAABMA=")] + // [InlineData(0, ByteOrder.BigEndian, "AAAAAAAABMA=", "Ag==", "AAAAAAAABMA=")] + // [InlineData(10, ByteOrder.BigEndian, "Ag==", "Ag==", "AAAAAAAABMA=")] + // public void MongoDBFunctions_ConvertToBsonBinaryDataFromIntWithOnErrorAndOnNull_should_work(int id, ByteOrder byteOrder, string expectedBase64, string onErrorBase64, string onNullBase64) + // { + // RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); + // + // var onErrorBinData = onErrorBase64 == null ? null : new BsonBinaryData(Convert.FromBase64String(onErrorBase64)); + // var onNullBinData = onNullBase64 == null ? null : new BsonBinaryData(Convert.FromBase64String(onNullBase64)); + // + // var collection = Fixture.Collection; + // var queryable = collection.AsQueryable() + // .Where(x => x.Id == id) + // .Select(x => Mql.ToBsonBinaryData(x.IntProperty, BsonBinarySubType.Binary, byteOrder, onErrorBinData, onNullBinData)); + // + // var onErrorString = onErrorBase64 == null ? "null" : $"BinData(0, '{onErrorBase64}')"; + // var onNullString = onNullBase64 == null ? "null" : $"BinData(0, '{onNullBase64}')"; + // var expectedStages = + // new[] + // { + // $"{{ $match : {{ _id : {id} }} }}", + // $"{{ $project: {{ _v : {{ $convert : {{ input : '$IntProperty', to : {{ type: 'binData', subtype: 0 }}, onError: {onErrorString}, onNull: {onNullString}, {ByteOrderToString(byteOrder)}}} }}, _id : 0 }} }}", + // }; + // + // var expectedResult = expectedBase64 is null? null : new BsonBinaryData(Convert.FromBase64String(expectedBase64)); + // AssertOutcome(collection, queryable, expectedStages, expectedResult); + // } [Theory] [InlineData(1, ByteOrder.BigEndian, null, "FormatException")] [InlineData(4, ByteOrder.LittleEndian,"ogIAAA==", null)] [InlineData(6, ByteOrder.BigEndian, "AAAAKg==", null )] - public void MongoDBFunctions_ConvertToLongDataFromInt_should_work(int id, ByteOrder byteOrder, string expectedBase64, string expectedException) + public void MongoDBFunctions_ConvertToBsonBinaryDataFromLong_should_work(int id, ByteOrder byteOrder, string expectedBase64, string expectedException) { RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); @@ -176,40 +174,40 @@ public void MongoDBFunctions_ConvertToLongDataFromInt_should_work(int id, ByteOr AssertOutcome(collection, queryable, expectedStages, expectedResult, expectedException); } - [Theory] - [InlineData(0, ByteOrder.LittleEndian, "AAAAAAAABMA=", "Ag==", "AAAAAAAABMA=")] - [InlineData(10, ByteOrder.LittleEndian, "Ag==", "Ag==", "AAAAAAAABMA=")] - [InlineData(0, ByteOrder.BigEndian, "AAAAAAAABMA=", "Ag==", "AAAAAAAABMA=")] - [InlineData(10, ByteOrder.BigEndian, "Ag==", "Ag==", "AAAAAAAABMA=")] - public void MongoDBFunctions_ConvertToLongDataFromIntWithOnErrorAndOnNull_should_work(int id, ByteOrder byteOrder, string expectedBase64, string onErrorBase64, string onNullBase64) - { - RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); - - var onErrorBinData = onErrorBase64 == null ? null : new BsonBinaryData(Convert.FromBase64String(onErrorBase64)); - var onNullBinData = onNullBase64 == null ? null : new BsonBinaryData(Convert.FromBase64String(onNullBase64)); - - var collection = Fixture.Collection; - var queryable = collection.AsQueryable() - .Where(x => x.Id == id) - .Select(x => Mql.ToBsonBinaryData(x.LongProperty, BsonBinarySubType.Binary, byteOrder, onErrorBinData, onNullBinData)); - - var onErrorString = onErrorBase64 == null ? "null" : $"BinData(0, '{onErrorBase64}')"; - var onNullString = onNullBase64 == null ? "null" : $"BinData(0, '{onNullBase64}')"; - var expectedStages = - new[] - { - $"{{ $match : {{ _id : {id} }} }}", - $"{{ $project: {{ _v : {{ $convert : {{ input : '$LongProperty', to : {{ type: 'binData', subtype: 0 }}, onError: {onErrorString}, onNull: {onNullString}, {ByteOrderToString(byteOrder)}}} }}, _id : 0 }} }}", - }; - - var expectedResult = expectedBase64 is null? null : new BsonBinaryData(Convert.FromBase64String(expectedBase64)); - AssertOutcome(collection, queryable, expectedStages, expectedResult); - } + // [Theory] + // [InlineData(0, ByteOrder.LittleEndian, "AAAAAAAABMA=", "Ag==", "AAAAAAAABMA=")] + // [InlineData(10, ByteOrder.LittleEndian, "Ag==", "Ag==", "AAAAAAAABMA=")] + // [InlineData(0, ByteOrder.BigEndian, "AAAAAAAABMA=", "Ag==", "AAAAAAAABMA=")] + // [InlineData(10, ByteOrder.BigEndian, "Ag==", "Ag==", "AAAAAAAABMA=")] + // public void MongoDBFunctions_ConvertToLongDataFromIntWithOnErrorAndOnNull_should_work(int id, ByteOrder byteOrder, string expectedBase64, string onErrorBase64, string onNullBase64) + // { + // RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); + // + // var onErrorBinData = onErrorBase64 == null ? null : new BsonBinaryData(Convert.FromBase64String(onErrorBase64)); + // var onNullBinData = onNullBase64 == null ? null : new BsonBinaryData(Convert.FromBase64String(onNullBase64)); + // + // var collection = Fixture.Collection; + // var queryable = collection.AsQueryable() + // .Where(x => x.Id == id) + // .Select(x => Mql.ToBsonBinaryData(x.LongProperty, BsonBinarySubType.Binary, byteOrder, onErrorBinData, onNullBinData)); + // + // var onErrorString = onErrorBase64 == null ? "null" : $"BinData(0, '{onErrorBase64}')"; + // var onNullString = onNullBase64 == null ? "null" : $"BinData(0, '{onNullBase64}')"; + // var expectedStages = + // new[] + // { + // $"{{ $match : {{ _id : {id} }} }}", + // $"{{ $project: {{ _v : {{ $convert : {{ input : '$LongProperty', to : {{ type: 'binData', subtype: 0 }}, onError: {onErrorString}, onNull: {onNullString}, {ByteOrderToString(byteOrder)}}} }}, _id : 0 }} }}", + // }; + // + // var expectedResult = expectedBase64 is null? null : new BsonBinaryData(Convert.FromBase64String(expectedBase64)); + // AssertOutcome(collection, queryable, expectedStages, expectedResult); + // } [Theory] [InlineData(1, null, "FormatException")] [InlineData(2, "867dee52-c331-484e-92d1-c56479b8e67e", null)] - public void MongoDBFunctions_ConvertToBinDataFromString_should_work(int id, string expectedGuidString, string expectedException) + public void MongoDBFunctions_ConvertToBsonBinaryDataFromString_should_work(int id, string expectedGuidString, string expectedException) { RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromString); @@ -229,34 +227,34 @@ public void MongoDBFunctions_ConvertToBinDataFromString_should_work(int id, stri AssertOutcome(collection, queryable, expectedStages, expectedResult, expectedException); } - [Theory] - [InlineData(0, "AAAAAAAABMA=", "Ag==", "AAAAAAAABMA=")] - [InlineData(10, "Ag==", "Ag==", "AAAAAAAABMA=")] - public void MongoDBFunctions_ConvertToBinDataFromStringWithOnErrorAndOnNull_should_work(int id, string expectedBase64, string onErrorBase64, string onNullBase64) - { - RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); - - var onErrorBinData = onErrorBase64 == null ? null : new BsonBinaryData(Convert.FromBase64String(onErrorBase64)); - var onNullBinData = onNullBase64 == null ? null : new BsonBinaryData(Convert.FromBase64String(onNullBase64)); - - var collection = Fixture.Collection; - var queryable = collection.AsQueryable() - .Where(x => x.Id == id) - .Select(x => Mql.ToBsonBinaryData(x.StringProperty, BsonBinarySubType.UuidStandard, "uuid", onErrorBinData, onNullBinData)); - - var onErrorString = onErrorBase64 == null ? "null" : $"BinData(0, '{onErrorBase64}')"; - var onNullString = onNullBase64 == null ? "null" : $"BinData(0, '{onNullBase64}')"; - - var expectedStages = - new[] - { - $"{{ $match : {{ _id : {id} }} }}", - $"{{ $project: {{ _v : {{ $convert : {{ input : '$StringProperty', to : {{ type: 'binData', subtype: 4 }}, onError: {onErrorString}, onNull: {onNullString}, format: 'uuid' }} }}, _id : 0 }} }}", - }; - - var expectedResult = expectedBase64 is null? null : new BsonBinaryData(Convert.FromBase64String(expectedBase64)); - AssertOutcome(collection, queryable, expectedStages, expectedResult); - } + // [Theory] + // [InlineData(0, "AAAAAAAABMA=", "Ag==", "AAAAAAAABMA=")] + // [InlineData(10, "Ag==", "Ag==", "AAAAAAAABMA=")] + // public void MongoDBFunctions_ConvertToBsonBinaryDataFromStringWithOnErrorAndOnNull_should_work(int id, string expectedBase64, string onErrorBase64, string onNullBase64) + // { + // RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); + // + // var onErrorBinData = onErrorBase64 == null ? null : new BsonBinaryData(Convert.FromBase64String(onErrorBase64)); + // var onNullBinData = onNullBase64 == null ? null : new BsonBinaryData(Convert.FromBase64String(onNullBase64)); + // + // var collection = Fixture.Collection; + // var queryable = collection.AsQueryable() + // .Where(x => x.Id == id) + // .Select(x => Mql.ToBsonBinaryData(x.StringProperty, BsonBinarySubType.UuidStandard, "uuid", onErrorBinData, onNullBinData)); + // + // var onErrorString = onErrorBase64 == null ? "null" : $"BinData(0, '{onErrorBase64}')"; + // var onNullString = onNullBase64 == null ? "null" : $"BinData(0, '{onNullBase64}')"; + // + // var expectedStages = + // new[] + // { + // $"{{ $match : {{ _id : {id} }} }}", + // $"{{ $project: {{ _v : {{ $convert : {{ input : '$StringProperty', to : {{ type: 'binData', subtype: 4 }}, onError: {onErrorString}, onNull: {onNullString}, format: 'uuid' }} }}, _id : 0 }} }}", + // }; + // + // var expectedResult = expectedBase64 is null? null : new BsonBinaryData(Convert.FromBase64String(expectedBase64)); + // AssertOutcome(collection, queryable, expectedStages, expectedResult); + // } // To Double @@ -264,7 +262,7 @@ public void MongoDBFunctions_ConvertToBinDataFromStringWithOnErrorAndOnNull_shou [InlineData(2, ByteOrder.BigEndian, null, "MongoCommandException")] [InlineData(3, ByteOrder.LittleEndian, -0.5, null)] [InlineData(5, ByteOrder.BigEndian, -2.5, null)] - public void MongoDBFunctions_ConvertToDoubleFromBinData_should_work(int id, ByteOrder byteOrder, double? expectedResult, string expectedException) + public void MongoDBFunctions_ConvertToDoubleFromBsonBinaryData_should_work(int id, ByteOrder byteOrder, double? expectedResult, string expectedException) { RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); @@ -283,33 +281,33 @@ public void MongoDBFunctions_ConvertToDoubleFromBinData_should_work(int id, Byte AssertOutcome(collection, queryable, expectedStages, expectedResult, expectedException); } - [Theory] - [InlineData(2, ByteOrder.LittleEndian, 15.2, 15.2, 22.3)] - [InlineData(0, ByteOrder.LittleEndian, 22.3, 15.2, 22.3)] - [InlineData(2, ByteOrder.BigEndian, 15.2, 15.2, 22.3)] - [InlineData(0, ByteOrder.BigEndian, 22.3, 15.2, 22.3)] - [InlineData(2, ByteOrder.LittleEndian, null, null, 22.3)] - [InlineData(0, ByteOrder.LittleEndian, null, 15.2, null)] - public void MongoDBFunctions_ConvertToDoubleFromBinDataWithOnErrorAndOnNull_should_work(int id, ByteOrder byteOrder, double? expectedResult, double? onError, double? onNull) - { - RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); - - var collection = Fixture.Collection; - var queryable = collection.AsQueryable() - .Where(x => x.Id == id) - .Select(x => Mql.ToNullableDouble(x.BinaryProperty, byteOrder, onError, onNull)); - - var onErrorString = onError == null ? "null" : onError.Value.ToString(NumberFormatInfo.InvariantInfo); - var onNullString = onNull == null ? "null" : onNull.Value.ToString(NumberFormatInfo.InvariantInfo); - var expectedStages = - new[] - { - $"{{ $match : {{ _id : {id} }} }}", - $"{{ $project: {{ _v : {{ $convert : {{ input : '$BinaryProperty', to : 'double', onError: {onErrorString}, onNull: {onNullString}, {ByteOrderToString(byteOrder)}}} }}, _id : 0 }} }}", - }; - - AssertOutcome(collection, queryable, expectedStages, expectedResult); - } + // [Theory] + // [InlineData(2, ByteOrder.LittleEndian, 15.2, 15.2, 22.3)] + // [InlineData(0, ByteOrder.LittleEndian, 22.3, 15.2, 22.3)] + // [InlineData(2, ByteOrder.BigEndian, 15.2, 15.2, 22.3)] + // [InlineData(0, ByteOrder.BigEndian, 22.3, 15.2, 22.3)] + // [InlineData(2, ByteOrder.LittleEndian, null, null, 22.3)] + // [InlineData(0, ByteOrder.LittleEndian, null, 15.2, null)] + // public void MongoDBFunctions_ConvertToDoubleFromBsonBinaryDataWithOnErrorAndOnNull_should_work(int id, ByteOrder byteOrder, double? expectedResult, double? onError, double? onNull) + // { + // RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); + // + // var collection = Fixture.Collection; + // var queryable = collection.AsQueryable() + // .Where(x => x.Id == id) + // .Select(x => Mql.ToNullableDouble(x.BinaryProperty, byteOrder, onError, onNull)); + // + // var onErrorString = onError == null ? "null" : onError.Value.ToString(NumberFormatInfo.InvariantInfo); + // var onNullString = onNull == null ? "null" : onNull.Value.ToString(NumberFormatInfo.InvariantInfo); + // var expectedStages = + // new[] + // { + // $"{{ $match : {{ _id : {id} }} }}", + // $"{{ $project: {{ _v : {{ $convert : {{ input : '$BinaryProperty', to : 'double', onError: {onErrorString}, onNull: {onNullString}, {ByteOrderToString(byteOrder)}}} }}, _id : 0 }} }}", + // }; + // + // AssertOutcome(collection, queryable, expectedStages, expectedResult); + // } // To Int @@ -317,7 +315,7 @@ public void MongoDBFunctions_ConvertToDoubleFromBinDataWithOnErrorAndOnNull_shou [InlineData(2, ByteOrder.LittleEndian, null, "MongoCommandException")] [InlineData(4, ByteOrder.LittleEndian, 674, null)] [InlineData(6, ByteOrder.BigEndian, 42, null)] - public void MongoDBFunctions_ConvertToIntFromBinData_should_work(int id, ByteOrder byteOrder, int? expectedResult, string expectedException) + public void MongoDBFunctions_ConvertToIntFromBsonBinaryData_should_work(int id, ByteOrder byteOrder, int? expectedResult, string expectedException) { RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); @@ -336,33 +334,33 @@ public void MongoDBFunctions_ConvertToIntFromBinData_should_work(int id, ByteOrd AssertOutcome(collection, queryable, expectedStages, expectedResult, expectedException); } - [Theory] - [InlineData(2, ByteOrder.LittleEndian, 15, 15, 22)] - [InlineData(0, ByteOrder.LittleEndian, 22, 15, 22)] - [InlineData(2, ByteOrder.BigEndian, 15, 15, 22)] - [InlineData(0, ByteOrder.BigEndian, 22, 15, 22)] - [InlineData(2, ByteOrder.LittleEndian, null, null, 22)] - [InlineData(0, ByteOrder.LittleEndian, null, 15, null)] - public void MongoDBFunctions_ConvertToIntFromBinDataWithOnErrorAndOnNull_should_work(int id, ByteOrder byteOrder, int? expectedResult, int? onError, int? onNull) - { - RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); - - var collection = Fixture.Collection; - var queryable = collection.AsQueryable() - .Where(x => x.Id == id) - .Select(x => Mql.ToNullableInt(x.BinaryProperty, byteOrder, onError, onNull)); - - var onErrorString = onError == null ? "null" : onError.Value.ToString(NumberFormatInfo.InvariantInfo); - var onNullString = onNull == null ? "null" : onNull.Value.ToString(NumberFormatInfo.InvariantInfo); - var expectedStages = - new[] - { - $"{{ $match : {{ _id : {id} }} }}", - $"{{ $project: {{ _v : {{ $convert : {{ input : '$BinaryProperty', to : 'int', onError: {onErrorString}, onNull: {onNullString}, {ByteOrderToString(byteOrder)} }} }}, _id : 0 }} }}", - }; - - AssertOutcome(collection, queryable, expectedStages, expectedResult); - } + // [Theory] + // [InlineData(2, ByteOrder.LittleEndian, 15, 15, 22)] + // [InlineData(0, ByteOrder.LittleEndian, 22, 15, 22)] + // [InlineData(2, ByteOrder.BigEndian, 15, 15, 22)] + // [InlineData(0, ByteOrder.BigEndian, 22, 15, 22)] + // [InlineData(2, ByteOrder.LittleEndian, null, null, 22)] + // [InlineData(0, ByteOrder.LittleEndian, null, 15, null)] + // public void MongoDBFunctions_ConvertToIntFromBsonBinaryDataWithOnErrorAndOnNull_should_work(int id, ByteOrder byteOrder, int? expectedResult, int? onError, int? onNull) + // { + // RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); + // + // var collection = Fixture.Collection; + // var queryable = collection.AsQueryable() + // .Where(x => x.Id == id) + // .Select(x => Mql.ToNullableInt(x.BinaryProperty, byteOrder, onError, onNull)); + // + // var onErrorString = onError == null ? "null" : onError.Value.ToString(NumberFormatInfo.InvariantInfo); + // var onNullString = onNull == null ? "null" : onNull.Value.ToString(NumberFormatInfo.InvariantInfo); + // var expectedStages = + // new[] + // { + // $"{{ $match : {{ _id : {id} }} }}", + // $"{{ $project: {{ _v : {{ $convert : {{ input : '$BinaryProperty', to : 'int', onError: {onErrorString}, onNull: {onNullString}, {ByteOrderToString(byteOrder)} }} }}, _id : 0 }} }}", + // }; + // + // AssertOutcome(collection, queryable, expectedStages, expectedResult); + // } // To Long @@ -370,7 +368,7 @@ public void MongoDBFunctions_ConvertToIntFromBinDataWithOnErrorAndOnNull_should_ [InlineData(2, ByteOrder.LittleEndian, null, "MongoCommandException")] [InlineData(4, ByteOrder.LittleEndian, (long)674, null)] [InlineData(6, ByteOrder.BigEndian, (long)42, null)] - public void MongoDBFunctions_ConvertToLongFromBinData_should_work(int id, ByteOrder byteOrder, long? expectedResult, string expectedException) + public void MongoDBFunctions_ConvertToLongFromBsonBinaryData_should_work(int id, ByteOrder byteOrder, long? expectedResult, string expectedException) { RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); @@ -389,40 +387,40 @@ public void MongoDBFunctions_ConvertToLongFromBinData_should_work(int id, ByteOr AssertOutcome(collection, queryable, expectedStages, expectedResult, expectedException); } - [Theory] - [InlineData(2, ByteOrder.LittleEndian, (long)15, (long)15, (long)22)] - [InlineData(0, ByteOrder.LittleEndian, (long)22, (long)15, (long)22)] - [InlineData(2, ByteOrder.BigEndian, (long)15, (long)15, (long)22)] - [InlineData(0, ByteOrder.BigEndian, (long)22, (long)15, (long)22)] - [InlineData(2, ByteOrder.LittleEndian, null, null, (long)22)] - [InlineData(0, ByteOrder.LittleEndian, null, (long)15, null)] - public void MongoDBFunctions_ConvertToLongFromBinDataWithOnErrorAndOnNull_should_work(int id, ByteOrder byteOrder, long? expectedResult, long? onError, long? onNull) - { - RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); - - var collection = Fixture.Collection; - var queryable = collection.AsQueryable() - .Where(x => x.Id == id) - .Select(x => Mql.ToNullableLong(x.BinaryProperty, byteOrder, onError, onNull)); - - var onErrorString = onError == null ? "null" : onError.Value.ToString(NumberFormatInfo.InvariantInfo); - var onNullString = onNull == null ? "null" : onNull.Value.ToString(NumberFormatInfo.InvariantInfo); - var expectedStages = - new[] - { - $"{{ $match : {{ _id : {id} }} }}", - $"{{ $project: {{ _v : {{ $convert : {{ input : '$BinaryProperty', to : 'long', onError: {onErrorString}, onNull: {onNullString}, {ByteOrderToString(byteOrder)} }} }}, _id : 0 }} }}", - }; - - AssertOutcome(collection, queryable, expectedStages, expectedResult); - } + // [Theory] + // [InlineData(2, ByteOrder.LittleEndian, (long)15, (long)15, (long)22)] + // [InlineData(0, ByteOrder.LittleEndian, (long)22, (long)15, (long)22)] + // [InlineData(2, ByteOrder.BigEndian, (long)15, (long)15, (long)22)] + // [InlineData(0, ByteOrder.BigEndian, (long)22, (long)15, (long)22)] + // [InlineData(2, ByteOrder.LittleEndian, null, null, (long)22)] + // [InlineData(0, ByteOrder.LittleEndian, null, (long)15, null)] + // public void MongoDBFunctions_ConvertToLongFromBsonBinaryDataWithOnErrorAndOnNull_should_work(int id, ByteOrder byteOrder, long? expectedResult, long? onError, long? onNull) + // { + // RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); + // + // var collection = Fixture.Collection; + // var queryable = collection.AsQueryable() + // .Where(x => x.Id == id) + // .Select(x => Mql.ToNullableLong(x.BinaryProperty, byteOrder, onError, onNull)); + // + // var onErrorString = onError == null ? "null" : onError.Value.ToString(NumberFormatInfo.InvariantInfo); + // var onNullString = onNull == null ? "null" : onNull.Value.ToString(NumberFormatInfo.InvariantInfo); + // var expectedStages = + // new[] + // { + // $"{{ $match : {{ _id : {id} }} }}", + // $"{{ $project: {{ _v : {{ $convert : {{ input : '$BinaryProperty', to : 'long', onError: {onErrorString}, onNull: {onNullString}, {ByteOrderToString(byteOrder)} }} }}, _id : 0 }} }}", + // }; + // + // AssertOutcome(collection, queryable, expectedStages, expectedResult); + // } // To String [Theory] [InlineData(2, "867dee52-c331-484e-92d1-c56479b8e67e", null)] [InlineData(1, null, "MongoCommandException")] - public void MongoDBFunctions_ConvertToStringFromBinData_should_work(int id, string expectedResult, string expectedException) + public void MongoDBFunctions_ConvertToStringFromBsonBinaryData_should_work(int id, string expectedResult, string expectedException) { RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromString); @@ -441,32 +439,32 @@ public void MongoDBFunctions_ConvertToStringFromBinData_should_work(int id, stri AssertOutcome(collection, queryable, expectedStages, expectedResult, expectedException); } - [Theory] - [InlineData(0, "onNull", "onError", "onNull")] - [InlineData(1, "onError", "onError", "onNull")] - [InlineData(0, null, "onError", null)] - [InlineData(1, null, null, "onNull")] - public void MongoDBFunctions_ConvertToStringFromBinDataWithOnErrorAndOnNull_should_work(int id, string expectedResult, string onError, string onNull) - { - RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromString); - - var collection = Fixture.Collection; - var queryable = collection.AsQueryable() - .Where(x => x.Id == id) - .Select(x => Mql.ToString(x.BinaryProperty, "uuid", onError, onNull)); - - var onErrorString = onError == null ? "null" : $"'{onError}'"; - var onNullString = onNull == null ? "null" : $"'{onNull}'"; - - var expectedStages = - new[] - { - $"{{ $match : {{ _id : {id} }} }}", - $"{{ $project: {{ _v : {{ $convert : {{ input : '$BinaryProperty', to : 'string' , onError: {onErrorString}, onNull: {onNullString}, format : 'uuid' }} }}, _id : 0 }} }}", - }; - - AssertOutcome(collection, queryable, expectedStages, expectedResult); - } + // [Theory] + // [InlineData(0, "onNull", "onError", "onNull")] + // [InlineData(1, "onError", "onError", "onNull")] + // [InlineData(0, null, "onError", null)] + // [InlineData(1, null, null, "onNull")] + // public void MongoDBFunctions_ConvertToStringFromBsonBinaryDataWithOnErrorAndOnNull_should_work(int id, string expectedResult, string onError, string onNull) + // { + // RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromString); + // + // var collection = Fixture.Collection; + // var queryable = collection.AsQueryable() + // .Where(x => x.Id == id) + // .Select(x => Mql.ToString(x.BinaryProperty, "uuid", onError, onNull)); + // + // var onErrorString = onError == null ? "null" : $"'{onError}'"; + // var onNullString = onNull == null ? "null" : $"'{onNull}'"; + // + // var expectedStages = + // new[] + // { + // $"{{ $match : {{ _id : {id} }} }}", + // $"{{ $project: {{ _v : {{ $convert : {{ input : '$BinaryProperty', to : 'string' , onError: {onErrorString}, onNull: {onNullString}, format : 'uuid' }} }}, _id : 0 }} }}", + // }; + // + // AssertOutcome(collection, queryable, expectedStages, expectedResult); + // } private void AssertOutcome(IMongoCollection collection, IQueryable queryable, From 16ec0b18e82585e52c68b4073853eb978cfe71de Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Wed, 26 Mar 2025 13:56:01 +0100 Subject: [PATCH 40/82] Improvements --- ...essionToAggregationExpressionTranslator.cs | 2 +- ...MethodToAggregationExpressionTranslator.cs | 4 +- ...dToAggregationExpressionTranslatorTests.cs | 90 +++++++++++++++++-- 3 files changed, 85 insertions(+), 11 deletions(-) diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodCallExpressionToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodCallExpressionToAggregationExpressionTranslator.cs index 4cdc67aac94..8b84588ea8c 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodCallExpressionToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodCallExpressionToAggregationExpressionTranslator.cs @@ -141,7 +141,7 @@ public static TranslatedExpression Translate(TranslationContext context, MethodC case "ToBsonBinaryData": case "ToDouble": case "ToInt": - case "TLong": + case "ToLong": case "ToNullableDouble": case "ToNullableInt": case "ToNullableLong": diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs index 9d3ae33a982..57960ebe26a 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs @@ -20,7 +20,9 @@ internal class ConvertMethodToAggregationExpressionTranslator ([MqlMethod.ToBinDataFromInt, MqlMethod.ToBinDataFromLong, MqlMethod.ToBinDataFromDouble, MqlMethod.ToBinDataFromNullableInt, MqlMethod.ToBinDataFromNullableLong, MqlMethod.ToBinDataFromNullableDouble], BsonValueSerializer.Instance, BsonType.Binary, null, 1, 2, null), ([MqlMethod.ToBinDataFromStringWithOptions], BsonValueSerializer.Instance, BsonType.Binary, 2, 1, null, 3), - ([MqlMethod.ToBinDataFromIntWithOptions, MqlMethod.ToBinDataFromLongWithOptions, MqlMethod.ToBinDataFromDoubleWithOptions], BsonValueSerializer.Instance, BsonType.Binary, null, 1, 2, 3), + ([MqlMethod.ToBinDataFromIntWithOptions, MqlMethod.ToBinDataFromLongWithOptions, MqlMethod.ToBinDataFromDoubleWithOptions, + MqlMethod.ToBinDataFromNullableIntWithOptions, MqlMethod.ToBinDataFromNullableLongWithOptions, MqlMethod.ToBinDataFromNullableDoubleWithOptions], + BsonValueSerializer.Instance, BsonType.Binary, null, 1, 2, 3), ([MqlMethod.ToDoubleFromBinData], DoubleSerializer.Instance, BsonType.Double, null, null, 1, null), ([MqlMethod.ToDoubleFromBinDataWithOptions], DoubleSerializer.Instance, BsonType.Double, null, null, 1, 2), diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs index 29f3f63c31e..5be62ed302b 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs @@ -45,7 +45,7 @@ public void MongoDBFunctions_ConvertToBsonBinaryDataFromDouble_should_work(int i var collection = Fixture.Collection; var queryable = collection.AsQueryable() .Where(x => x.Id == id) - .Select(x => Mql.ToBsonBinaryData(x.DoubleProperty, BsonBinarySubType.Binary, byteOrder)); + .Select(x => Mql.ToBsonBinaryData(x.NullableDoubleProperty, BsonBinarySubType.Binary, byteOrder)); var expectedStages = new[] @@ -99,7 +99,7 @@ public void MongoDBFunctions_ConvertToBsonBinaryDataFromInt_should_work(int id, var collection = Fixture.Collection; var queryable = collection.AsQueryable() .Where(x => x.Id == id) - .Select(x => Mql.ToBsonBinaryData(x.IntProperty, BsonBinarySubType.Binary, byteOrder)); + .Select(x => Mql.ToBsonBinaryData(x.NullableIntProperty, BsonBinarySubType.Binary, byteOrder)); var expectedStages = new[] @@ -153,7 +153,7 @@ public void MongoDBFunctions_ConvertToBsonBinaryDataFromLong_should_work(int id, var collection = Fixture.Collection; var queryable = collection.AsQueryable() .Where(x => x.Id == id) - .Select(x => Mql.ToBsonBinaryData(x.LongProperty, BsonBinarySubType.Binary, byteOrder)); + .Select(x => Mql.ToBsonBinaryData(x.NullableLongProperty, BsonBinarySubType.Binary, byteOrder)); var expectedStages = new[] @@ -258,11 +258,34 @@ public void MongoDBFunctions_ConvertToBsonBinaryDataFromString_should_work(int i // To Double + [Theory] + [InlineData(2, ByteOrder.BigEndian, 0, "MongoCommandException")] + [InlineData(3, ByteOrder.LittleEndian, -0.5, null)] + [InlineData(5, ByteOrder.BigEndian, -2.5, null)] + public void MongoDBFunctions_ConvertToDoubleFromBsonBinaryData_should_work(int id, ByteOrder byteOrder, double expectedResult, string expectedException) + { + RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); + + var collection = Fixture.Collection; + var queryable = collection.AsQueryable() + .Where(x => x.Id == id) + .Select(x => Mql.ToDouble(x.BinaryProperty, byteOrder)); + + var expectedStages = + new[] + { + $"{{ $match : {{ _id : {id} }} }}", + $"{{ $project: {{ _v : {{ $convert : {{ input : '$BinaryProperty', to : 'double', {ByteOrderToString(byteOrder)} }} }}, _id : 0 }} }}", + }; + + AssertOutcome(collection, queryable, expectedStages, expectedResult, expectedException); + } + [Theory] [InlineData(2, ByteOrder.BigEndian, null, "MongoCommandException")] [InlineData(3, ByteOrder.LittleEndian, -0.5, null)] [InlineData(5, ByteOrder.BigEndian, -2.5, null)] - public void MongoDBFunctions_ConvertToDoubleFromBsonBinaryData_should_work(int id, ByteOrder byteOrder, double? expectedResult, string expectedException) + public void MongoDBFunctions_ConvertToNullableDoubleFromBsonBinaryData_should_work(int id, ByteOrder byteOrder, double? expectedResult, string expectedException) { RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); @@ -311,11 +334,34 @@ public void MongoDBFunctions_ConvertToDoubleFromBsonBinaryData_should_work(int i // To Int + [Theory] + [InlineData(2, ByteOrder.LittleEndian, 0, "MongoCommandException")] + [InlineData(4, ByteOrder.LittleEndian, 674, null)] + [InlineData(6, ByteOrder.BigEndian, 42, null)] + public void MongoDBFunctions_ConvertToIntFromBsonBinaryData_should_work(int id, ByteOrder byteOrder, int expectedResult, string expectedException) + { + RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); + + var collection = Fixture.Collection; + var queryable = collection.AsQueryable() + .Where(x => x.Id == id) + .Select(x => Mql.ToInt(x.BinaryProperty, byteOrder)); + + var expectedStages = + new[] + { + $"{{ $match : {{ _id : {id} }} }}", + $"{{ $project: {{ _v : {{ $convert : {{ input : '$BinaryProperty', to : 'int', {ByteOrderToString(byteOrder)} }} }}, _id : 0 }} }}", + }; + + AssertOutcome(collection, queryable, expectedStages, expectedResult, expectedException); + } + [Theory] [InlineData(2, ByteOrder.LittleEndian, null, "MongoCommandException")] [InlineData(4, ByteOrder.LittleEndian, 674, null)] [InlineData(6, ByteOrder.BigEndian, 42, null)] - public void MongoDBFunctions_ConvertToIntFromBsonBinaryData_should_work(int id, ByteOrder byteOrder, int? expectedResult, string expectedException) + public void MongoDBFunctions_ConvertToNullableIntFromBsonBinaryData_should_work(int id, ByteOrder byteOrder, int? expectedResult, string expectedException) { RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); @@ -364,11 +410,34 @@ public void MongoDBFunctions_ConvertToIntFromBsonBinaryData_should_work(int id, // To Long + [Theory] + [InlineData(2, ByteOrder.LittleEndian, 0, "MongoCommandException")] + [InlineData(4, ByteOrder.LittleEndian, (long)674, null)] + [InlineData(6, ByteOrder.BigEndian, (long)42, null)] + public void MongoDBFunctions_ConvertToLongFromBsonBinaryData_should_work(int id, ByteOrder byteOrder, long expectedResult, string expectedException) + { + RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); + + var collection = Fixture.Collection; + var queryable = collection.AsQueryable() + .Where(x => x.Id == id) + .Select(x => Mql.ToLong(x.BinaryProperty, byteOrder)); + + var expectedStages = + new[] + { + $"{{ $match : {{ _id : {id} }} }}", + $"{{ $project: {{ _v : {{ $convert : {{ input : '$BinaryProperty', to : 'long', {ByteOrderToString(byteOrder)} }} }}, _id : 0 }} }}", + }; + + AssertOutcome(collection, queryable, expectedStages, expectedResult, expectedException); + } + [Theory] [InlineData(2, ByteOrder.LittleEndian, null, "MongoCommandException")] [InlineData(4, ByteOrder.LittleEndian, (long)674, null)] [InlineData(6, ByteOrder.BigEndian, (long)42, null)] - public void MongoDBFunctions_ConvertToLongFromBsonBinaryData_should_work(int id, ByteOrder byteOrder, long? expectedResult, string expectedException) + public void MongoDBFunctions_ConvertToNullableLongFromBsonBinaryData_should_work(int id, ByteOrder byteOrder, long? expectedResult, string expectedException) { RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); @@ -521,9 +590,12 @@ public class TestClass { public int Id { get; set; } public BsonBinaryData BinaryProperty { get; set; } - public double? DoubleProperty { get; set; } - public int? IntProperty { get; set; } - public long? LongProperty { get; set; } + public double DoubleProperty { get; set; } + public int IntProperty { get; set; } + public long LongProperty { get; set; } + public double? NullableDoubleProperty { get; set; } + public int? NullableIntProperty { get; set; } + public long? NullableLongProperty { get; set; } public string StringProperty { get; set; } } } From 698690f775c95bf9cadeb631848935932b8dd316 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Wed, 26 Mar 2025 13:57:45 +0100 Subject: [PATCH 41/82] TestMethod renaming --- ...dToAggregationExpressionTranslatorTests.cs | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs index 5be62ed302b..de53261cdc0 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs @@ -38,7 +38,7 @@ public ConvertMethodToAggregationExpressionTranslatorTests(ClassFixture fixture) [InlineData(1, ByteOrder.BigEndian, null, "FormatException")] [InlineData(3, ByteOrder.LittleEndian,"AAAAAAAA4L8=", null)] [InlineData(5, ByteOrder.BigEndian, "wAQAAAAAAAA=", null )] - public void MongoDBFunctions_ConvertToBsonBinaryDataFromDouble_should_work(int id, ByteOrder byteOrder, string expectedBase64, string expectedException) + public void MongoDBFunctions_ToBsonBinaryDataFromDouble_should_work(int id, ByteOrder byteOrder, string expectedBase64, string expectedException) { RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); @@ -63,7 +63,7 @@ public void MongoDBFunctions_ConvertToBsonBinaryDataFromDouble_should_work(int i // [InlineData(10, ByteOrder.LittleEndian, "Ag==", "Ag==", "AAAAAAAABMA=")] // [InlineData(0, ByteOrder.BigEndian, "AAAAAAAABMA=", "Ag==", "AAAAAAAABMA=")] // [InlineData(10, ByteOrder.BigEndian, "Ag==", "Ag==", "AAAAAAAABMA=")] - // public void MongoDBFunctions_ConvertToBsonBinaryDataFromDoubleWithOnErrorAndOnNull_should_work(int id, ByteOrder byteOrder, string expectedBase64, string onErrorBase64, string onNullBase64) + // public void MongoDBFunctions_ToBsonBinaryDataFromDoubleWithOnErrorAndOnNull_should_work(int id, ByteOrder byteOrder, string expectedBase64, string onErrorBase64, string onNullBase64) // { // RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); // @@ -92,7 +92,7 @@ public void MongoDBFunctions_ConvertToBsonBinaryDataFromDouble_should_work(int i [InlineData(1, ByteOrder.BigEndian, null, "FormatException")] [InlineData(4, ByteOrder.LittleEndian,"ogIAAA==", null)] [InlineData(6, ByteOrder.BigEndian, "AAAAKg==", null )] - public void MongoDBFunctions_ConvertToBsonBinaryDataFromInt_should_work(int id, ByteOrder byteOrder, string expectedBase64, string expectedException) + public void MongoDBFunctions_ToBsonBinaryDataFromInt_should_work(int id, ByteOrder byteOrder, string expectedBase64, string expectedException) { RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); @@ -117,7 +117,7 @@ public void MongoDBFunctions_ConvertToBsonBinaryDataFromInt_should_work(int id, // [InlineData(10, ByteOrder.LittleEndian, "Ag==", "Ag==", "AAAAAAAABMA=")] // [InlineData(0, ByteOrder.BigEndian, "AAAAAAAABMA=", "Ag==", "AAAAAAAABMA=")] // [InlineData(10, ByteOrder.BigEndian, "Ag==", "Ag==", "AAAAAAAABMA=")] - // public void MongoDBFunctions_ConvertToBsonBinaryDataFromIntWithOnErrorAndOnNull_should_work(int id, ByteOrder byteOrder, string expectedBase64, string onErrorBase64, string onNullBase64) + // public void MongoDBFunctions_ToBsonBinaryDataFromIntWithOnErrorAndOnNull_should_work(int id, ByteOrder byteOrder, string expectedBase64, string onErrorBase64, string onNullBase64) // { // RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); // @@ -146,7 +146,7 @@ public void MongoDBFunctions_ConvertToBsonBinaryDataFromInt_should_work(int id, [InlineData(1, ByteOrder.BigEndian, null, "FormatException")] [InlineData(4, ByteOrder.LittleEndian,"ogIAAA==", null)] [InlineData(6, ByteOrder.BigEndian, "AAAAKg==", null )] - public void MongoDBFunctions_ConvertToBsonBinaryDataFromLong_should_work(int id, ByteOrder byteOrder, string expectedBase64, string expectedException) + public void MongoDBFunctions_ToBsonBinaryDataFromLong_should_work(int id, ByteOrder byteOrder, string expectedBase64, string expectedException) { RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); @@ -179,7 +179,7 @@ public void MongoDBFunctions_ConvertToBsonBinaryDataFromLong_should_work(int id, // [InlineData(10, ByteOrder.LittleEndian, "Ag==", "Ag==", "AAAAAAAABMA=")] // [InlineData(0, ByteOrder.BigEndian, "AAAAAAAABMA=", "Ag==", "AAAAAAAABMA=")] // [InlineData(10, ByteOrder.BigEndian, "Ag==", "Ag==", "AAAAAAAABMA=")] - // public void MongoDBFunctions_ConvertToLongDataFromIntWithOnErrorAndOnNull_should_work(int id, ByteOrder byteOrder, string expectedBase64, string onErrorBase64, string onNullBase64) + // public void MongoDBFunctions_ToLongDataFromIntWithOnErrorAndOnNull_should_work(int id, ByteOrder byteOrder, string expectedBase64, string onErrorBase64, string onNullBase64) // { // RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); // @@ -207,7 +207,7 @@ public void MongoDBFunctions_ConvertToBsonBinaryDataFromLong_should_work(int id, [Theory] [InlineData(1, null, "FormatException")] [InlineData(2, "867dee52-c331-484e-92d1-c56479b8e67e", null)] - public void MongoDBFunctions_ConvertToBsonBinaryDataFromString_should_work(int id, string expectedGuidString, string expectedException) + public void MongoDBFunctions_ToBsonBinaryDataFromString_should_work(int id, string expectedGuidString, string expectedException) { RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromString); @@ -230,7 +230,7 @@ public void MongoDBFunctions_ConvertToBsonBinaryDataFromString_should_work(int i // [Theory] // [InlineData(0, "AAAAAAAABMA=", "Ag==", "AAAAAAAABMA=")] // [InlineData(10, "Ag==", "Ag==", "AAAAAAAABMA=")] - // public void MongoDBFunctions_ConvertToBsonBinaryDataFromStringWithOnErrorAndOnNull_should_work(int id, string expectedBase64, string onErrorBase64, string onNullBase64) + // public void MongoDBFunctions_ToBsonBinaryDataFromStringWithOnErrorAndOnNull_should_work(int id, string expectedBase64, string onErrorBase64, string onNullBase64) // { // RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); // @@ -262,7 +262,7 @@ public void MongoDBFunctions_ConvertToBsonBinaryDataFromString_should_work(int i [InlineData(2, ByteOrder.BigEndian, 0, "MongoCommandException")] [InlineData(3, ByteOrder.LittleEndian, -0.5, null)] [InlineData(5, ByteOrder.BigEndian, -2.5, null)] - public void MongoDBFunctions_ConvertToDoubleFromBsonBinaryData_should_work(int id, ByteOrder byteOrder, double expectedResult, string expectedException) + public void MongoDBFunctions_ToDoubleFromBsonBinaryData_should_work(int id, ByteOrder byteOrder, double expectedResult, string expectedException) { RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); @@ -285,7 +285,7 @@ public void MongoDBFunctions_ConvertToDoubleFromBsonBinaryData_should_work(int i [InlineData(2, ByteOrder.BigEndian, null, "MongoCommandException")] [InlineData(3, ByteOrder.LittleEndian, -0.5, null)] [InlineData(5, ByteOrder.BigEndian, -2.5, null)] - public void MongoDBFunctions_ConvertToNullableDoubleFromBsonBinaryData_should_work(int id, ByteOrder byteOrder, double? expectedResult, string expectedException) + public void MongoDBFunctions_ToNullableDoubleFromBsonBinaryData_should_work(int id, ByteOrder byteOrder, double? expectedResult, string expectedException) { RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); @@ -311,7 +311,7 @@ public void MongoDBFunctions_ConvertToNullableDoubleFromBsonBinaryData_should_wo // [InlineData(0, ByteOrder.BigEndian, 22.3, 15.2, 22.3)] // [InlineData(2, ByteOrder.LittleEndian, null, null, 22.3)] // [InlineData(0, ByteOrder.LittleEndian, null, 15.2, null)] - // public void MongoDBFunctions_ConvertToDoubleFromBsonBinaryDataWithOnErrorAndOnNull_should_work(int id, ByteOrder byteOrder, double? expectedResult, double? onError, double? onNull) + // public void MongoDBFunctions_ToDoubleFromBsonBinaryDataWithOnErrorAndOnNull_should_work(int id, ByteOrder byteOrder, double? expectedResult, double? onError, double? onNull) // { // RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); // @@ -338,7 +338,7 @@ public void MongoDBFunctions_ConvertToNullableDoubleFromBsonBinaryData_should_wo [InlineData(2, ByteOrder.LittleEndian, 0, "MongoCommandException")] [InlineData(4, ByteOrder.LittleEndian, 674, null)] [InlineData(6, ByteOrder.BigEndian, 42, null)] - public void MongoDBFunctions_ConvertToIntFromBsonBinaryData_should_work(int id, ByteOrder byteOrder, int expectedResult, string expectedException) + public void MongoDBFunctions_ToIntFromBsonBinaryData_should_work(int id, ByteOrder byteOrder, int expectedResult, string expectedException) { RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); @@ -361,7 +361,7 @@ public void MongoDBFunctions_ConvertToIntFromBsonBinaryData_should_work(int id, [InlineData(2, ByteOrder.LittleEndian, null, "MongoCommandException")] [InlineData(4, ByteOrder.LittleEndian, 674, null)] [InlineData(6, ByteOrder.BigEndian, 42, null)] - public void MongoDBFunctions_ConvertToNullableIntFromBsonBinaryData_should_work(int id, ByteOrder byteOrder, int? expectedResult, string expectedException) + public void MongoDBFunctions_ToNullableIntFromBsonBinaryData_should_work(int id, ByteOrder byteOrder, int? expectedResult, string expectedException) { RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); @@ -387,7 +387,7 @@ public void MongoDBFunctions_ConvertToNullableIntFromBsonBinaryData_should_work( // [InlineData(0, ByteOrder.BigEndian, 22, 15, 22)] // [InlineData(2, ByteOrder.LittleEndian, null, null, 22)] // [InlineData(0, ByteOrder.LittleEndian, null, 15, null)] - // public void MongoDBFunctions_ConvertToIntFromBsonBinaryDataWithOnErrorAndOnNull_should_work(int id, ByteOrder byteOrder, int? expectedResult, int? onError, int? onNull) + // public void MongoDBFunctions_ToIntFromBsonBinaryDataWithOnErrorAndOnNull_should_work(int id, ByteOrder byteOrder, int? expectedResult, int? onError, int? onNull) // { // RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); // @@ -414,7 +414,7 @@ public void MongoDBFunctions_ConvertToNullableIntFromBsonBinaryData_should_work( [InlineData(2, ByteOrder.LittleEndian, 0, "MongoCommandException")] [InlineData(4, ByteOrder.LittleEndian, (long)674, null)] [InlineData(6, ByteOrder.BigEndian, (long)42, null)] - public void MongoDBFunctions_ConvertToLongFromBsonBinaryData_should_work(int id, ByteOrder byteOrder, long expectedResult, string expectedException) + public void MongoDBFunctions_ToLongFromBsonBinaryData_should_work(int id, ByteOrder byteOrder, long expectedResult, string expectedException) { RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); @@ -437,7 +437,7 @@ public void MongoDBFunctions_ConvertToLongFromBsonBinaryData_should_work(int id, [InlineData(2, ByteOrder.LittleEndian, null, "MongoCommandException")] [InlineData(4, ByteOrder.LittleEndian, (long)674, null)] [InlineData(6, ByteOrder.BigEndian, (long)42, null)] - public void MongoDBFunctions_ConvertToNullableLongFromBsonBinaryData_should_work(int id, ByteOrder byteOrder, long? expectedResult, string expectedException) + public void MongoDBFunctions_ToNullableLongFromBsonBinaryData_should_work(int id, ByteOrder byteOrder, long? expectedResult, string expectedException) { RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); @@ -463,7 +463,7 @@ public void MongoDBFunctions_ConvertToNullableLongFromBsonBinaryData_should_work // [InlineData(0, ByteOrder.BigEndian, (long)22, (long)15, (long)22)] // [InlineData(2, ByteOrder.LittleEndian, null, null, (long)22)] // [InlineData(0, ByteOrder.LittleEndian, null, (long)15, null)] - // public void MongoDBFunctions_ConvertToLongFromBsonBinaryDataWithOnErrorAndOnNull_should_work(int id, ByteOrder byteOrder, long? expectedResult, long? onError, long? onNull) + // public void MongoDBFunctions_ToLongFromBsonBinaryDataWithOnErrorAndOnNull_should_work(int id, ByteOrder byteOrder, long? expectedResult, long? onError, long? onNull) // { // RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); // @@ -489,7 +489,7 @@ public void MongoDBFunctions_ConvertToNullableLongFromBsonBinaryData_should_work [Theory] [InlineData(2, "867dee52-c331-484e-92d1-c56479b8e67e", null)] [InlineData(1, null, "MongoCommandException")] - public void MongoDBFunctions_ConvertToStringFromBsonBinaryData_should_work(int id, string expectedResult, string expectedException) + public void MongoDBFunctions_ToStringFromBsonBinaryData_should_work(int id, string expectedResult, string expectedException) { RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromString); @@ -513,7 +513,7 @@ public void MongoDBFunctions_ConvertToStringFromBsonBinaryData_should_work(int i // [InlineData(1, "onError", "onError", "onNull")] // [InlineData(0, null, "onError", null)] // [InlineData(1, null, null, "onNull")] - // public void MongoDBFunctions_ConvertToStringFromBsonBinaryDataWithOnErrorAndOnNull_should_work(int id, string expectedResult, string onError, string onNull) + // public void MongoDBFunctions_ToStringFromBsonBinaryDataWithOnErrorAndOnNull_should_work(int id, string expectedResult, string onError, string onNull) // { // RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromString); // From 10f05e69d4ee29aa6b074048b106eeb4f991bc12 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Wed, 26 Mar 2025 14:47:37 +0100 Subject: [PATCH 42/82] Improved methods --- ...dToAggregationExpressionTranslatorTests.cs | 359 ++++++++++++------ 1 file changed, 252 insertions(+), 107 deletions(-) diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs index de53261cdc0..115857168b2 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs @@ -15,6 +15,7 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; using MongoDB.Bson; using MongoDB.Driver.Core.Misc; @@ -282,6 +283,7 @@ public void MongoDBFunctions_ToDoubleFromBsonBinaryData_should_work(int id, Byte } [Theory] + [InlineData(0, ByteOrder.BigEndian, null, null)] [InlineData(2, ByteOrder.BigEndian, null, "MongoCommandException")] [InlineData(3, ByteOrder.LittleEndian, -0.5, null)] [InlineData(5, ByteOrder.BigEndian, -2.5, null)] @@ -304,33 +306,74 @@ public void MongoDBFunctions_ToNullableDoubleFromBsonBinaryData_should_work(int AssertOutcome(collection, queryable, expectedStages, expectedResult, expectedException); } - // [Theory] - // [InlineData(2, ByteOrder.LittleEndian, 15.2, 15.2, 22.3)] - // [InlineData(0, ByteOrder.LittleEndian, 22.3, 15.2, 22.3)] - // [InlineData(2, ByteOrder.BigEndian, 15.2, 15.2, 22.3)] - // [InlineData(0, ByteOrder.BigEndian, 22.3, 15.2, 22.3)] - // [InlineData(2, ByteOrder.LittleEndian, null, null, 22.3)] - // [InlineData(0, ByteOrder.LittleEndian, null, 15.2, null)] - // public void MongoDBFunctions_ToDoubleFromBsonBinaryDataWithOnErrorAndOnNull_should_work(int id, ByteOrder byteOrder, double? expectedResult, double? onError, double? onNull) - // { - // RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); - // - // var collection = Fixture.Collection; - // var queryable = collection.AsQueryable() - // .Where(x => x.Id == id) - // .Select(x => Mql.ToNullableDouble(x.BinaryProperty, byteOrder, onError, onNull)); - // - // var onErrorString = onError == null ? "null" : onError.Value.ToString(NumberFormatInfo.InvariantInfo); - // var onNullString = onNull == null ? "null" : onNull.Value.ToString(NumberFormatInfo.InvariantInfo); - // var expectedStages = - // new[] - // { - // $"{{ $match : {{ _id : {id} }} }}", - // $"{{ $project: {{ _v : {{ $convert : {{ input : '$BinaryProperty', to : 'double', onError: {onErrorString}, onNull: {onNullString}, {ByteOrderToString(byteOrder)}}} }}, _id : 0 }} }}", - // }; - // - // AssertOutcome(collection, queryable, expectedStages, expectedResult); - // } + [Theory] + [InlineData(2, ByteOrder.LittleEndian, 15.2, true, 15.2, true, 22.3)] + [InlineData(0, ByteOrder.LittleEndian, 22.3, true, 15.2, true, 22.3)] + [InlineData(2, ByteOrder.BigEndian, 15.2,true, 15.2, true,22.3)] + [InlineData(0, ByteOrder.BigEndian, 22.3, true, 15.2, true, 22.3)] + [InlineData(2, ByteOrder.LittleEndian, 0, true, 0, true, 22.3)] + [InlineData(0, ByteOrder.LittleEndian, 0, true, 15.2, true, 0)] + [InlineData(0, ByteOrder.LittleEndian, 0, false, 15.2, true, 0)] + public void MongoDBFunctions_ToDoubleFromBsonBinaryDataWithOnErrorAndOnNull_should_work(int id, ByteOrder byteOrder, double expectedResult, bool setOnError, double onError, bool setOnNull, double onNull) + { + RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); + + var options = new ConvertOptions(); + if (setOnError) options.OnError = onError; + if (setOnNull) options.OnNull = onNull; + + var collection = Fixture.Collection; + var queryable = collection.AsQueryable() + .Where(x => x.Id == id) + .Select(x => Mql.ToDouble(x.BinaryProperty, byteOrder, options)); + + var onErrorStr = setOnError ? $"onError: {Format(onError)}," : ""; + var onNullStr = setOnNull ? $"onNull: {Format(onNull)}," : ""; + + var expectedStages = + new[] + { + $"{{ $match : {{ _id : {id} }} }}", + $"{{ $project: {{ _v : {{ $convert : {{ input : '$BinaryProperty', to : 'double', {onErrorStr} {onNullStr} {ByteOrderToString(byteOrder)}}} }}, _id : 0 }} }}", + }; + + AssertOutcome(collection, queryable, expectedStages, expectedResult); + } + + [Theory] + [InlineData(2, ByteOrder.LittleEndian, 15.2, true, 15.2, true, 22.3)] + [InlineData(0, ByteOrder.LittleEndian, 22.3, true, 15.2, true, 22.3)] + [InlineData(2, ByteOrder.BigEndian, 15.2,true, 15.2, true,22.3)] + [InlineData(0, ByteOrder.BigEndian, 22.3, true, 15.2, true, 22.3)] + [InlineData(2, ByteOrder.LittleEndian, null, true, null, true, 22.3)] + [InlineData(0, ByteOrder.LittleEndian, null, true, 15.2, true, null)] + [InlineData(2, ByteOrder.LittleEndian, null, true, null, false, 22.3)] + [InlineData(0, ByteOrder.LittleEndian, null, false, 15.2, true, null)] + public void MongoDBFunctions_ToNullableDoubleFromBsonBinaryDataWithOnErrorAndOnNull_should_work(int id, ByteOrder byteOrder, double? expectedResult, bool setOnError, double? onError, bool setOnNull, double? onNull) + { + RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); + + var options = new ConvertOptions(); + if (setOnError) options.OnError = onError; + if (setOnNull) options.OnNull = onNull; + + var collection = Fixture.Collection; + var queryable = collection.AsQueryable() + .Where(x => x.Id == id) + .Select(x => Mql.ToNullableDouble(x.BinaryProperty, byteOrder, options)); + + var onErrorStr = setOnError ? $"onError: {Format(onError)}," : ""; + var onNullStr = setOnNull ? $"onNull: {Format(onNull)}," : ""; + + var expectedStages = + new[] + { + $"{{ $match : {{ _id : {id} }} }}", + $"{{ $project: {{ _v : {{ $convert : {{ input : '$BinaryProperty', to : 'double', {onErrorStr} {onNullStr} {ByteOrderToString(byteOrder)}}} }}, _id : 0 }} }}", + }; + + AssertOutcome(collection, queryable, expectedStages, expectedResult); + } // To Int @@ -358,6 +401,7 @@ public void MongoDBFunctions_ToIntFromBsonBinaryData_should_work(int id, ByteOrd } [Theory] + [InlineData(0, ByteOrder.BigEndian, null, null)] [InlineData(2, ByteOrder.LittleEndian, null, "MongoCommandException")] [InlineData(4, ByteOrder.LittleEndian, 674, null)] [InlineData(6, ByteOrder.BigEndian, 42, null)] @@ -380,33 +424,74 @@ public void MongoDBFunctions_ToNullableIntFromBsonBinaryData_should_work(int id, AssertOutcome(collection, queryable, expectedStages, expectedResult, expectedException); } - // [Theory] - // [InlineData(2, ByteOrder.LittleEndian, 15, 15, 22)] - // [InlineData(0, ByteOrder.LittleEndian, 22, 15, 22)] - // [InlineData(2, ByteOrder.BigEndian, 15, 15, 22)] - // [InlineData(0, ByteOrder.BigEndian, 22, 15, 22)] - // [InlineData(2, ByteOrder.LittleEndian, null, null, 22)] - // [InlineData(0, ByteOrder.LittleEndian, null, 15, null)] - // public void MongoDBFunctions_ToIntFromBsonBinaryDataWithOnErrorAndOnNull_should_work(int id, ByteOrder byteOrder, int? expectedResult, int? onError, int? onNull) - // { - // RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); - // - // var collection = Fixture.Collection; - // var queryable = collection.AsQueryable() - // .Where(x => x.Id == id) - // .Select(x => Mql.ToNullableInt(x.BinaryProperty, byteOrder, onError, onNull)); - // - // var onErrorString = onError == null ? "null" : onError.Value.ToString(NumberFormatInfo.InvariantInfo); - // var onNullString = onNull == null ? "null" : onNull.Value.ToString(NumberFormatInfo.InvariantInfo); - // var expectedStages = - // new[] - // { - // $"{{ $match : {{ _id : {id} }} }}", - // $"{{ $project: {{ _v : {{ $convert : {{ input : '$BinaryProperty', to : 'int', onError: {onErrorString}, onNull: {onNullString}, {ByteOrderToString(byteOrder)} }} }}, _id : 0 }} }}", - // }; - // - // AssertOutcome(collection, queryable, expectedStages, expectedResult); - // } + [Theory] + [InlineData(2, ByteOrder.LittleEndian, 15, true, 15, true, 22)] + [InlineData(0, ByteOrder.LittleEndian, 22, true, 15, true, 22)] + [InlineData(2, ByteOrder.BigEndian, 15, true, 15, true, 22)] + [InlineData(0, ByteOrder.BigEndian, 22, true, 15, true, 22)] + [InlineData(2, ByteOrder.LittleEndian, 0, true, 0, true, 22)] + [InlineData(0, ByteOrder.LittleEndian, 0, true, 15, true, 0)] + [InlineData(0, ByteOrder.LittleEndian, 0, false, 15, true, 0)] + public void MongoDBFunctions_ToIntFromBsonBinaryDataWithOnErrorAndOnNull_should_work(int id, ByteOrder byteOrder, int expectedResult, bool setOnError, int onError, bool setOnNull, int onNull) + { + RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); + + var options = new ConvertOptions(); + if (setOnError) options.OnError = onError; + if (setOnNull) options.OnNull = onNull; + + var collection = Fixture.Collection; + var queryable = collection.AsQueryable() + .Where(x => x.Id == id) + .Select(x => Mql.ToInt(x.BinaryProperty, byteOrder, options)); + + var onErrorStr = setOnError ? $"onError: {Format(onError)}," : ""; + var onNullStr = setOnNull ? $"onNull: {Format(onNull)}," : ""; + + var expectedStages = + new[] + { + $"{{ $match : {{ _id : {id} }} }}", + $"{{ $project: {{ _v : {{ $convert : {{ input : '$BinaryProperty', to : 'int', {onErrorStr} {onNullStr} {ByteOrderToString(byteOrder)} }} }}, _id : 0 }} }}", + }; + + AssertOutcome(collection, queryable, expectedStages, expectedResult); + } + + [Theory] + [InlineData(2, ByteOrder.LittleEndian, 15, true, 15, true, 22)] + [InlineData(0, ByteOrder.LittleEndian, 22, true, 15, true, 22)] + [InlineData(2, ByteOrder.BigEndian, 15, true, 15, true, 22)] + [InlineData(0, ByteOrder.BigEndian, 22, true, 15, true, 22)] + [InlineData(2, ByteOrder.LittleEndian, null, true, null, true, 22)] + [InlineData(0, ByteOrder.LittleEndian, null, true, 15, true, null)] + [InlineData(2, ByteOrder.LittleEndian, null, true, null, false, 22)] + [InlineData(0, ByteOrder.LittleEndian, null, false, 15, true, null)] + public void MongoDBFunctions_ToNullableIntFromBsonBinaryDataWithOnErrorAndOnNull_should_work(int id, ByteOrder byteOrder, int? expectedResult, bool setOnError, int? onError, bool setOnNull, int? onNull) + { + RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); + + var options = new ConvertOptions(); + if (setOnError) options.OnError = onError; + if (setOnNull) options.OnNull = onNull; + + var collection = Fixture.Collection; + var queryable = collection.AsQueryable() + .Where(x => x.Id == id) + .Select(x => Mql.ToNullableInt(x.BinaryProperty, byteOrder, options)); + + var onErrorStr = setOnError ? $"onError: {Format(onError)}," : ""; + var onNullStr = setOnNull ? $"onNull: {Format(onNull)}," : ""; + + var expectedStages = + new[] + { + $"{{ $match : {{ _id : {id} }} }}", + $"{{ $project: {{ _v : {{ $convert : {{ input : '$BinaryProperty', to : 'int', {onErrorStr} {onNullStr} {ByteOrderToString(byteOrder)} }} }}, _id : 0 }} }}", + }; + + AssertOutcome(collection, queryable, expectedStages, expectedResult); + } // To Long @@ -434,6 +519,7 @@ public void MongoDBFunctions_ToLongFromBsonBinaryData_should_work(int id, ByteOr } [Theory] + [InlineData(0, ByteOrder.BigEndian, null, null)] [InlineData(2, ByteOrder.LittleEndian, null, "MongoCommandException")] [InlineData(4, ByteOrder.LittleEndian, (long)674, null)] [InlineData(6, ByteOrder.BigEndian, (long)42, null)] @@ -456,33 +542,74 @@ public void MongoDBFunctions_ToNullableLongFromBsonBinaryData_should_work(int id AssertOutcome(collection, queryable, expectedStages, expectedResult, expectedException); } - // [Theory] - // [InlineData(2, ByteOrder.LittleEndian, (long)15, (long)15, (long)22)] - // [InlineData(0, ByteOrder.LittleEndian, (long)22, (long)15, (long)22)] - // [InlineData(2, ByteOrder.BigEndian, (long)15, (long)15, (long)22)] - // [InlineData(0, ByteOrder.BigEndian, (long)22, (long)15, (long)22)] - // [InlineData(2, ByteOrder.LittleEndian, null, null, (long)22)] - // [InlineData(0, ByteOrder.LittleEndian, null, (long)15, null)] - // public void MongoDBFunctions_ToLongFromBsonBinaryDataWithOnErrorAndOnNull_should_work(int id, ByteOrder byteOrder, long? expectedResult, long? onError, long? onNull) - // { - // RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); - // - // var collection = Fixture.Collection; - // var queryable = collection.AsQueryable() - // .Where(x => x.Id == id) - // .Select(x => Mql.ToNullableLong(x.BinaryProperty, byteOrder, onError, onNull)); - // - // var onErrorString = onError == null ? "null" : onError.Value.ToString(NumberFormatInfo.InvariantInfo); - // var onNullString = onNull == null ? "null" : onNull.Value.ToString(NumberFormatInfo.InvariantInfo); - // var expectedStages = - // new[] - // { - // $"{{ $match : {{ _id : {id} }} }}", - // $"{{ $project: {{ _v : {{ $convert : {{ input : '$BinaryProperty', to : 'long', onError: {onErrorString}, onNull: {onNullString}, {ByteOrderToString(byteOrder)} }} }}, _id : 0 }} }}", - // }; - // - // AssertOutcome(collection, queryable, expectedStages, expectedResult); - // } + [Theory] + [InlineData(2, ByteOrder.LittleEndian, (long)15, true, (long)15, true, (long)22)] + [InlineData(0, ByteOrder.LittleEndian, (long)22, true, (long)15, true, (long)22)] + [InlineData(2, ByteOrder.BigEndian, (long)15, true, (long)15, true, (long)22)] + [InlineData(0, ByteOrder.BigEndian, (long)22, true, (long)15, true, (long)22)] + [InlineData(2, ByteOrder.LittleEndian, 0, true, 0, true, (long)22)] + [InlineData(0, ByteOrder.LittleEndian, 0, true, (long)15, true, 0)] + [InlineData(0, ByteOrder.LittleEndian, 0, false, (long)15, true, 0)] + public void MongoDBFunctions_ToLongFromBsonBinaryDataWithOnErrorAndOnNull_should_work(int id, ByteOrder byteOrder, long expectedResult, bool setOnError, long onError, bool setOnNull, long onNull) + { + RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); + + var options = new ConvertOptions(); + if (setOnError) options.OnError = onError; + if (setOnNull) options.OnNull = onNull; + + var collection = Fixture.Collection; + var queryable = collection.AsQueryable() + .Where(x => x.Id == id) + .Select(x => Mql.ToLong(x.BinaryProperty, byteOrder, options)); + + var onErrorStr = setOnError ? $"onError: {Format(onError)}," : ""; + var onNullStr = setOnNull ? $"onNull: {Format(onNull)}," : ""; + + var expectedStages = + new[] + { + $"{{ $match : {{ _id : {id} }} }}", + $"{{ $project: {{ _v : {{ $convert : {{ input : '$BinaryProperty', to : 'long', {onErrorStr} {onNullStr} {ByteOrderToString(byteOrder)} }} }}, _id : 0 }} }}", + }; + + AssertOutcome(collection, queryable, expectedStages, expectedResult); + } + + [Theory] + [InlineData(2, ByteOrder.LittleEndian, (long)15, true, (long)15, true, (long)22)] + [InlineData(0, ByteOrder.LittleEndian, (long)22, true, (long)15, true, (long)22)] + [InlineData(2, ByteOrder.BigEndian, (long)15, true, (long)15, true, (long)22)] + [InlineData(0, ByteOrder.BigEndian, (long)22, true, (long)15, true, (long)22)] + [InlineData(2, ByteOrder.LittleEndian, null, true, null, true, (long)22)] + [InlineData(0, ByteOrder.LittleEndian, null, true, (long)15, true, null)] + [InlineData(2, ByteOrder.LittleEndian, null, true, null, false, (long)22)] + [InlineData(0, ByteOrder.LittleEndian, null, false, (long)15, true, null)] + public void MongoDBFunctions_ToNullableLongFromBsonBinaryDataWithOnErrorAndOnNull_should_work(int id, ByteOrder byteOrder, long? expectedResult, bool setOnError, long? onError, bool setOnNull, long? onNull) + { + RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); + + var options = new ConvertOptions(); + if (setOnError) options.OnError = onError; + if (setOnNull) options.OnNull = onNull; + + var collection = Fixture.Collection; + var queryable = collection.AsQueryable() + .Where(x => x.Id == id) + .Select(x => Mql.ToNullableLong(x.BinaryProperty, byteOrder, options)); + + var onErrorStr = setOnError ? $"onError: {Format(onError)}," : ""; + var onNullStr = setOnNull ? $"onNull: {Format(onNull)}," : ""; + + var expectedStages = + new[] + { + $"{{ $match : {{ _id : {id} }} }}", + $"{{ $project: {{ _v : {{ $convert : {{ input : '$BinaryProperty', to : 'long', {onErrorStr} {onNullStr} {ByteOrderToString(byteOrder)} }} }}, _id : 0 }} }}", + }; + + AssertOutcome(collection, queryable, expectedStages, expectedResult); + } // To String @@ -508,32 +635,38 @@ public void MongoDBFunctions_ToStringFromBsonBinaryData_should_work(int id, stri AssertOutcome(collection, queryable, expectedStages, expectedResult, expectedException); } - // [Theory] - // [InlineData(0, "onNull", "onError", "onNull")] - // [InlineData(1, "onError", "onError", "onNull")] - // [InlineData(0, null, "onError", null)] - // [InlineData(1, null, null, "onNull")] - // public void MongoDBFunctions_ToStringFromBsonBinaryDataWithOnErrorAndOnNull_should_work(int id, string expectedResult, string onError, string onNull) - // { - // RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromString); - // - // var collection = Fixture.Collection; - // var queryable = collection.AsQueryable() - // .Where(x => x.Id == id) - // .Select(x => Mql.ToString(x.BinaryProperty, "uuid", onError, onNull)); - // - // var onErrorString = onError == null ? "null" : $"'{onError}'"; - // var onNullString = onNull == null ? "null" : $"'{onNull}'"; - // - // var expectedStages = - // new[] - // { - // $"{{ $match : {{ _id : {id} }} }}", - // $"{{ $project: {{ _v : {{ $convert : {{ input : '$BinaryProperty', to : 'string' , onError: {onErrorString}, onNull: {onNullString}, format : 'uuid' }} }}, _id : 0 }} }}", - // }; - // - // AssertOutcome(collection, queryable, expectedStages, expectedResult); - // } + [Theory] + [InlineData(0, "onNull", true, "onError", true, "onNull")] + [InlineData(1, "onError", true, "onError", true, "onNull")] + [InlineData(0, null, true, "onError", true, null)] + [InlineData(1, null, true, null, true, "onNull")] + [InlineData(0, null, false, "onError", true, null)] + [InlineData(1, null, true, null, false, "onNull")] + public void MongoDBFunctions_ToStringFromBsonBinaryDataWithOnErrorAndOnNull_should_work(int id, string expectedResult, bool setOnError, string onError, bool setOnNull, string onNull) + { + RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromString); + + var options = new ConvertOptions(); + if (setOnError) options.OnError = onError; + if (setOnNull) options.OnNull = onNull; + + var collection = Fixture.Collection; + var queryable = collection.AsQueryable() + .Where(x => x.Id == id) + .Select(x => Mql.ToString(x.BinaryProperty, "uuid", options)); + + var onErrorStr = setOnError ? $"onError: {Format(onError)}," : ""; + var onNullStr = setOnNull ? $"onNull: {Format(onNull)}," : ""; + + var expectedStages = + new[] + { + $"{{ $match : {{ _id : {id} }} }}", + $"{{ $project: {{ _v : {{ $convert : {{ input : '$BinaryProperty', to : 'string', {onErrorStr} {onNullStr} format : 'uuid' }} }}, _id : 0 }} }}", + }; + + AssertOutcome(collection, queryable, expectedStages, expectedResult); + } private void AssertOutcome(IMongoCollection collection, IQueryable queryable, @@ -571,6 +704,18 @@ private string ByteOrderToString(ByteOrder byteOrder) return $"byteOrder: '{byteOrderString}'"; } + private static string Format(double? value) => + value?.ToString(NumberFormatInfo.InvariantInfo) ?? "null"; + + private static string Format(int? value) => + value?.ToString(NumberFormatInfo.InvariantInfo) ?? "null"; + + private static string Format(long? value) => + value?.ToString(NumberFormatInfo.InvariantInfo) ?? "null"; + + private static string Format(string value) => + value is null ? "null" : $"'{value}'"; + public sealed class ClassFixture : MongoCollectionFixture { protected override IEnumerable InitialData => From 98fd0e14af1cf8459a85ed34b58cafbf11f0d729 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Wed, 26 Mar 2025 15:11:58 +0100 Subject: [PATCH 43/82] Other improvements --- ...dToAggregationExpressionTranslatorTests.cs | 122 +++++++++++++++--- 1 file changed, 107 insertions(+), 15 deletions(-) diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs index 115857168b2..74c4a0d1291 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs @@ -36,9 +36,9 @@ public ConvertMethodToAggregationExpressionTranslatorTests(ClassFixture fixture) // To BinData [Theory] - [InlineData(1, ByteOrder.BigEndian, null, "FormatException")] [InlineData(3, ByteOrder.LittleEndian,"AAAAAAAA4L8=", null)] [InlineData(5, ByteOrder.BigEndian, "wAQAAAAAAAA=", null )] + [InlineData(10, ByteOrder.BigEndian, null, "MongoCommandException")] public void MongoDBFunctions_ToBsonBinaryDataFromDouble_should_work(int id, ByteOrder byteOrder, string expectedBase64, string expectedException) { RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); @@ -46,7 +46,7 @@ public void MongoDBFunctions_ToBsonBinaryDataFromDouble_should_work(int id, Byte var collection = Fixture.Collection; var queryable = collection.AsQueryable() .Where(x => x.Id == id) - .Select(x => Mql.ToBsonBinaryData(x.NullableDoubleProperty, BsonBinarySubType.Binary, byteOrder)); + .Select(x => Mql.ToBsonBinaryData(x.DoubleProperty, BsonBinarySubType.Binary, byteOrder)); var expectedStages = new[] @@ -55,7 +55,32 @@ public void MongoDBFunctions_ToBsonBinaryDataFromDouble_should_work(int id, Byte $"{{ $project: {{ _v : {{ $convert : {{ input : '$DoubleProperty', to : {{ type: 'binData', subtype: 0 }}, {ByteOrderToString(byteOrder)} }} }}, _id : 0 }} }}", }; - var expectedResult = expectedBase64 is null? null : new BsonBinaryData(Convert.FromBase64String(expectedBase64)); + BsonValue expectedResult = expectedBase64 is null? BsonNull.Value : new BsonBinaryData(Convert.FromBase64String(expectedBase64)); + AssertOutcome(collection, queryable, expectedStages, expectedResult, expectedException); + } + + [Theory] + [InlineData(1, ByteOrder.LittleEndian,null, null)] + [InlineData(3, ByteOrder.LittleEndian,"AAAAAAAA4L8=", null)] + [InlineData(5, ByteOrder.BigEndian, "wAQAAAAAAAA=", null )] + [InlineData(10, ByteOrder.BigEndian, null, "MongoCommandException")] + public void MongoDBFunctions_ToBsonBinaryDataFromNullableDouble_should_work(int id, ByteOrder byteOrder, string expectedBase64, string expectedException) + { + RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); + + var collection = Fixture.Collection; + var queryable = collection.AsQueryable() + .Where(x => x.Id == id) + .Select(x => Mql.ToBsonBinaryData(x.NullableDoubleProperty, BsonBinarySubType.Binary, byteOrder)); + + var expectedStages = + new[] + { + $"{{ $match : {{ _id : {id} }} }}", + $"{{ $project: {{ _v : {{ $convert : {{ input : '$NullableDoubleProperty', to : {{ type: 'binData', subtype: 0 }}, {ByteOrderToString(byteOrder)} }} }}, _id : 0 }} }}", + }; + + BsonValue expectedResult = expectedBase64 is null? BsonNull.Value : new BsonBinaryData(Convert.FromBase64String(expectedBase64)); AssertOutcome(collection, queryable, expectedStages, expectedResult, expectedException); } @@ -90,9 +115,9 @@ public void MongoDBFunctions_ToBsonBinaryDataFromDouble_should_work(int id, Byte // } [Theory] - [InlineData(1, ByteOrder.BigEndian, null, "FormatException")] [InlineData(4, ByteOrder.LittleEndian,"ogIAAA==", null)] [InlineData(6, ByteOrder.BigEndian, "AAAAKg==", null )] + [InlineData(10, ByteOrder.BigEndian, null, "MongoCommandException")] public void MongoDBFunctions_ToBsonBinaryDataFromInt_should_work(int id, ByteOrder byteOrder, string expectedBase64, string expectedException) { RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); @@ -100,7 +125,7 @@ public void MongoDBFunctions_ToBsonBinaryDataFromInt_should_work(int id, ByteOrd var collection = Fixture.Collection; var queryable = collection.AsQueryable() .Where(x => x.Id == id) - .Select(x => Mql.ToBsonBinaryData(x.NullableIntProperty, BsonBinarySubType.Binary, byteOrder)); + .Select(x => Mql.ToBsonBinaryData(x.IntProperty, BsonBinarySubType.Binary, byteOrder)); var expectedStages = new[] @@ -109,7 +134,32 @@ public void MongoDBFunctions_ToBsonBinaryDataFromInt_should_work(int id, ByteOrd $"{{ $project: {{ _v : {{ $convert : {{ input : '$IntProperty', to : {{ type: 'binData', subtype: 0 }}, {ByteOrderToString(byteOrder)} }} }}, _id : 0 }} }}", }; - var expectedResult = expectedBase64 is null? null : new BsonBinaryData(Convert.FromBase64String(expectedBase64)); + BsonValue expectedResult = expectedBase64 is null? BsonNull.Value : new BsonBinaryData(Convert.FromBase64String(expectedBase64)); + AssertOutcome(collection, queryable, expectedStages, expectedResult, expectedException); + } + + [Theory] + [InlineData(1, ByteOrder.LittleEndian,null, null)] + [InlineData(4, ByteOrder.LittleEndian,"ogIAAA==", null)] + [InlineData(6, ByteOrder.BigEndian, "AAAAKg==", null )] + [InlineData(10, ByteOrder.BigEndian, null, "MongoCommandException")] + public void MongoDBFunctions_ToBsonBinaryDataFromNullableInt_should_work(int id, ByteOrder byteOrder, string expectedBase64, string expectedException) + { + RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); + + var collection = Fixture.Collection; + var queryable = collection.AsQueryable() + .Where(x => x.Id == id) + .Select(x => Mql.ToBsonBinaryData(x.NullableIntProperty, BsonBinarySubType.Binary, byteOrder)); + + var expectedStages = + new[] + { + $"{{ $match : {{ _id : {id} }} }}", + $"{{ $project: {{ _v : {{ $convert : {{ input : '$NullableIntProperty', to : {{ type: 'binData', subtype: 0 }}, {ByteOrderToString(byteOrder)} }} }}, _id : 0 }} }}", + }; + + BsonValue expectedResult = expectedBase64 is null? BsonNull.Value : new BsonBinaryData(Convert.FromBase64String(expectedBase64)); AssertOutcome(collection, queryable, expectedStages, expectedResult, expectedException); } @@ -144,9 +194,9 @@ public void MongoDBFunctions_ToBsonBinaryDataFromInt_should_work(int id, ByteOrd // } [Theory] - [InlineData(1, ByteOrder.BigEndian, null, "FormatException")] [InlineData(4, ByteOrder.LittleEndian,"ogIAAA==", null)] [InlineData(6, ByteOrder.BigEndian, "AAAAKg==", null )] + [InlineData(10, ByteOrder.BigEndian, null, "MongoCommandException")] public void MongoDBFunctions_ToBsonBinaryDataFromLong_should_work(int id, ByteOrder byteOrder, string expectedBase64, string expectedException) { RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); @@ -154,7 +204,7 @@ public void MongoDBFunctions_ToBsonBinaryDataFromLong_should_work(int id, ByteOr var collection = Fixture.Collection; var queryable = collection.AsQueryable() .Where(x => x.Id == id) - .Select(x => Mql.ToBsonBinaryData(x.NullableLongProperty, BsonBinarySubType.Binary, byteOrder)); + .Select(x => Mql.ToBsonBinaryData(x.LongProperty, BsonBinarySubType.Binary, byteOrder)); var expectedStages = new[] @@ -163,7 +213,44 @@ public void MongoDBFunctions_ToBsonBinaryDataFromLong_should_work(int id, ByteOr $"{{ $project: {{ _v : {{ $convert : {{ input : '$LongProperty', to : {{ type: 'binData', subtype: 0 }}, {ByteOrderToString(byteOrder)} }} }}, _id : 0 }} }}", }; - BsonBinaryData expectedResult = null; + BsonValue expectedResult = null; + if (expectedBase64 is not null) + { + //$convert to bindata returns always 8 bytes when from long + var expectedBytes = new byte[8]; + Array.Copy(Convert.FromBase64String(expectedBase64), 0, expectedBytes, byteOrder is ByteOrder.LittleEndian ? 0 : 4, 4); + expectedResult = new BsonBinaryData(expectedBytes); + } + else + { + expectedResult = BsonNull.Value; + } + + AssertOutcome(collection, queryable, expectedStages, expectedResult, expectedException); + } + + [Theory] + [InlineData(1, ByteOrder.LittleEndian,null, null)] + [InlineData(4, ByteOrder.LittleEndian,"ogIAAA==", null)] + [InlineData(6, ByteOrder.BigEndian, "AAAAKg==", null )] + [InlineData(10, ByteOrder.BigEndian, null, "MongoCommandException")] + public void MongoDBFunctions_ToBsonBinaryDataFromNullableLong_should_work(int id, ByteOrder byteOrder, string expectedBase64, string expectedException) + { + RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); + + var collection = Fixture.Collection; + var queryable = collection.AsQueryable() + .Where(x => x.Id == id) + .Select(x => Mql.ToBsonBinaryData(x.NullableLongProperty, BsonBinarySubType.Binary, byteOrder)); + + var expectedStages = + new[] + { + $"{{ $match : {{ _id : {id} }} }}", + $"{{ $project: {{ _v : {{ $convert : {{ input : '$NullableLongProperty', to : {{ type: 'binData', subtype: 0 }}, {ByteOrderToString(byteOrder)} }} }}, _id : 0 }} }}", + }; + + BsonValue expectedResult = null; if (expectedBase64 is not null) { //$convert to bindata returns always 8 bytes when from long @@ -171,6 +258,10 @@ public void MongoDBFunctions_ToBsonBinaryDataFromLong_should_work(int id, ByteOr Array.Copy(Convert.FromBase64String(expectedBase64), 0, expectedBytes, byteOrder is ByteOrder.LittleEndian ? 0 : 4, 4); expectedResult = new BsonBinaryData(expectedBytes); } + else + { + expectedResult = BsonNull.Value; + } AssertOutcome(collection, queryable, expectedStages, expectedResult, expectedException); } @@ -206,8 +297,8 @@ public void MongoDBFunctions_ToBsonBinaryDataFromLong_should_work(int id, ByteOr // } [Theory] - [InlineData(1, null, "FormatException")] [InlineData(2, "867dee52-c331-484e-92d1-c56479b8e67e", null)] + [InlineData(10, null, "MongoCommandException")] public void MongoDBFunctions_ToBsonBinaryDataFromString_should_work(int id, string expectedGuidString, string expectedException) { RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromString); @@ -723,11 +814,12 @@ public sealed class ClassFixture : MongoCollectionFixture Date: Wed, 26 Mar 2025 15:54:58 +0100 Subject: [PATCH 44/82] Almost finished tests --- ...dToAggregationExpressionTranslatorTests.cs | 374 ++++++++++++------ 1 file changed, 251 insertions(+), 123 deletions(-) diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs index 74c4a0d1291..5bbccb4e5ab 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs @@ -84,35 +84,75 @@ public void MongoDBFunctions_ToBsonBinaryDataFromNullableDouble_should_work(int AssertOutcome(collection, queryable, expectedStages, expectedResult, expectedException); } - // [Theory] - // [InlineData(0, ByteOrder.LittleEndian, "AAAAAAAABMA=", "Ag==", "AAAAAAAABMA=")] - // [InlineData(10, ByteOrder.LittleEndian, "Ag==", "Ag==", "AAAAAAAABMA=")] - // [InlineData(0, ByteOrder.BigEndian, "AAAAAAAABMA=", "Ag==", "AAAAAAAABMA=")] - // [InlineData(10, ByteOrder.BigEndian, "Ag==", "Ag==", "AAAAAAAABMA=")] - // public void MongoDBFunctions_ToBsonBinaryDataFromDoubleWithOnErrorAndOnNull_should_work(int id, ByteOrder byteOrder, string expectedBase64, string onErrorBase64, string onNullBase64) - // { - // RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); - // - // var onErrorBinData = onErrorBase64 == null ? null : new BsonBinaryData(Convert.FromBase64String(onErrorBase64)); - // var onNullBinData = onNullBase64 == null ? null : new BsonBinaryData(Convert.FromBase64String(onNullBase64)); - // - // var collection = Fixture.Collection; - // var queryable = collection.AsQueryable() - // .Where(x => x.Id == id) - // .Select(x => Mql.ToBsonBinaryData(x.DoubleProperty, BsonBinarySubType.Binary, byteOrder, onErrorBinData, onNullBinData)); - // - // var onErrorString = onErrorBase64 == null ? "null" : $"BinData(0, '{onErrorBase64}')"; - // var onNullString = onNullBase64 == null ? "null" : $"BinData(0, '{onNullBase64}')"; - // var expectedStages = - // new[] - // { - // $"{{ $match : {{ _id : {id} }} }}", - // $"{{ $project: {{ _v : {{ $convert : {{ input : '$DoubleProperty', to : {{ type: 'binData', subtype: 0 }}, onError: {onErrorString}, onNull: {onNullString}, {ByteOrderToString(byteOrder)}}} }}, _id : 0 }} }}", - // }; - // - // var expectedResult = expectedBase64 is null? null : new BsonBinaryData(Convert.FromBase64String(expectedBase64)); - // AssertOutcome(collection, queryable, expectedStages, expectedResult); - // } + [Theory] + [InlineData(0, ByteOrder.LittleEndian, "AAAAAAAABMA=", true, "Ag==", true, "AAAAAAAABMA=")] + [InlineData(10, ByteOrder.LittleEndian, "Ag==", true, "Ag==", true, "AAAAAAAABMA=")] + [InlineData(0, ByteOrder.BigEndian, "AAAAAAAABMA=", true, "Ag==", true, "AAAAAAAABMA=")] + [InlineData(10, ByteOrder.BigEndian, "Ag==", true, "Ag==", true, "AAAAAAAABMA=")] + [InlineData(0, ByteOrder.BigEndian, null, true, "Ag==", true, null)] + [InlineData(10, ByteOrder.BigEndian, null, true, null, true, "AAAAAAAABMA=")] + public void MongoDBFunctions_ToBsonBinaryDataFromDoubleWithOptions_should_work(int id, ByteOrder byteOrder, string expectedBase64, bool setOnError, string onErrorBase64, bool setOnNull, string onNullBase64) + { + RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); + + var options = new ConvertOptions(); + if (setOnError) options.OnError = onErrorBase64 == null ? BsonNull.Value : new BsonBinaryData(Convert.FromBase64String(onErrorBase64)); + if (setOnNull) options.OnNull = onNullBase64 == null ? BsonNull.Value : new BsonBinaryData(Convert.FromBase64String(onNullBase64)); + + var collection = Fixture.Collection; + var queryable = collection.AsQueryable() + .Where(x => x.Id == id) + .Select(x => Mql.ToBsonBinaryData(x.DoubleProperty, BsonBinarySubType.Binary, byteOrder, options)); + + var onErrorStr = setOnError ? $"onError: {FormatBase64(onErrorBase64)}," : ""; + var onNullStr = setOnNull ? $"onNull: {FormatBase64(onNullBase64)}," : ""; + + var expectedStages = + new[] + { + $"{{ $match : {{ _id : {id} }} }}", + $"{{ $project: {{ _v : {{ $convert : {{ input : '$DoubleProperty', to : {{ type: 'binData', subtype: 0 }}, {onErrorStr} {onNullStr} {ByteOrderToString(byteOrder)}}} }}, _id : 0 }} }}", + }; + + BsonValue expectedResult = expectedBase64 is null? BsonNull.Value : new BsonBinaryData(Convert.FromBase64String(expectedBase64)); + AssertOutcome(collection, queryable, expectedStages, expectedResult); + } + + [Theory] + [InlineData(0, ByteOrder.LittleEndian, "AAAAAAAABMA=", true, "Ag==", true, "AAAAAAAABMA=")] + [InlineData(10, ByteOrder.LittleEndian, "Ag==", true, "Ag==", true, "AAAAAAAABMA=")] + [InlineData(0, ByteOrder.BigEndian, "AAAAAAAABMA=", true, "Ag==", true, "AAAAAAAABMA=")] + [InlineData(10, ByteOrder.BigEndian, "Ag==", true, "Ag==", true, "AAAAAAAABMA=")] + [InlineData(0, ByteOrder.BigEndian, null, true, "Ag==", true, null)] + [InlineData(10, ByteOrder.BigEndian, null, true, null, true, "AAAAAAAABMA=")] + [InlineData(0, ByteOrder.BigEndian, null, false, "Ag==", true, null)] + [InlineData(10, ByteOrder.BigEndian, null, true, null, false, "AAAAAAAABMA=")] + public void MongoDBFunctions_ToBsonBinaryDataFromNullableDoubleWithOptions_should_work(int id, ByteOrder byteOrder, string expectedBase64, bool setOnError, string onErrorBase64, bool setOnNull, string onNullBase64) + { + RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); + + var options = new ConvertOptions(); + if (setOnError) options.OnError = onErrorBase64 == null ? BsonNull.Value : new BsonBinaryData(Convert.FromBase64String(onErrorBase64)); + if (setOnNull) options.OnNull = onNullBase64 == null ? BsonNull.Value : new BsonBinaryData(Convert.FromBase64String(onNullBase64)); + + var collection = Fixture.Collection; + var queryable = collection.AsQueryable() + .Where(x => x.Id == id) + .Select(x => Mql.ToBsonBinaryData(x.NullableDoubleProperty, BsonBinarySubType.Binary, byteOrder, options)); + + var onErrorStr = setOnError ? $"onError: {FormatBase64(onErrorBase64)}," : ""; + var onNullStr = setOnNull ? $"onNull: {FormatBase64(onNullBase64)}," : ""; + + var expectedStages = + new[] + { + $"{{ $match : {{ _id : {id} }} }}", + $"{{ $project: {{ _v : {{ $convert : {{ input : '$NullableDoubleProperty', to : {{ type: 'binData', subtype: 0 }}, {onErrorStr} {onNullStr} {ByteOrderToString(byteOrder)}}} }}, _id : 0 }} }}", + }; + + BsonValue expectedResult = expectedBase64 is null? BsonNull.Value : new BsonBinaryData(Convert.FromBase64String(expectedBase64)); + AssertOutcome(collection, queryable, expectedStages, expectedResult); + } [Theory] [InlineData(4, ByteOrder.LittleEndian,"ogIAAA==", null)] @@ -163,35 +203,75 @@ public void MongoDBFunctions_ToBsonBinaryDataFromNullableInt_should_work(int id, AssertOutcome(collection, queryable, expectedStages, expectedResult, expectedException); } - // [Theory] - // [InlineData(0, ByteOrder.LittleEndian, "AAAAAAAABMA=", "Ag==", "AAAAAAAABMA=")] - // [InlineData(10, ByteOrder.LittleEndian, "Ag==", "Ag==", "AAAAAAAABMA=")] - // [InlineData(0, ByteOrder.BigEndian, "AAAAAAAABMA=", "Ag==", "AAAAAAAABMA=")] - // [InlineData(10, ByteOrder.BigEndian, "Ag==", "Ag==", "AAAAAAAABMA=")] - // public void MongoDBFunctions_ToBsonBinaryDataFromIntWithOnErrorAndOnNull_should_work(int id, ByteOrder byteOrder, string expectedBase64, string onErrorBase64, string onNullBase64) - // { - // RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); - // - // var onErrorBinData = onErrorBase64 == null ? null : new BsonBinaryData(Convert.FromBase64String(onErrorBase64)); - // var onNullBinData = onNullBase64 == null ? null : new BsonBinaryData(Convert.FromBase64String(onNullBase64)); - // - // var collection = Fixture.Collection; - // var queryable = collection.AsQueryable() - // .Where(x => x.Id == id) - // .Select(x => Mql.ToBsonBinaryData(x.IntProperty, BsonBinarySubType.Binary, byteOrder, onErrorBinData, onNullBinData)); - // - // var onErrorString = onErrorBase64 == null ? "null" : $"BinData(0, '{onErrorBase64}')"; - // var onNullString = onNullBase64 == null ? "null" : $"BinData(0, '{onNullBase64}')"; - // var expectedStages = - // new[] - // { - // $"{{ $match : {{ _id : {id} }} }}", - // $"{{ $project: {{ _v : {{ $convert : {{ input : '$IntProperty', to : {{ type: 'binData', subtype: 0 }}, onError: {onErrorString}, onNull: {onNullString}, {ByteOrderToString(byteOrder)}}} }}, _id : 0 }} }}", - // }; - // - // var expectedResult = expectedBase64 is null? null : new BsonBinaryData(Convert.FromBase64String(expectedBase64)); - // AssertOutcome(collection, queryable, expectedStages, expectedResult); - // } + [Theory] + [InlineData(0, ByteOrder.LittleEndian, "AAAAAAAABMA=", true, "Ag==", true, "AAAAAAAABMA=")] + [InlineData(10, ByteOrder.LittleEndian, "Ag==", true, "Ag==", true, "AAAAAAAABMA=")] + [InlineData(0, ByteOrder.BigEndian, "AAAAAAAABMA=", true, "Ag==", true, "AAAAAAAABMA=")] + [InlineData(10, ByteOrder.BigEndian, "Ag==", true, "Ag==", true, "AAAAAAAABMA=")] + [InlineData(0, ByteOrder.BigEndian, null, true, "Ag==", true, null)] + [InlineData(10, ByteOrder.BigEndian, null, true, null, true, "AAAAAAAABMA=")] + public void MongoDBFunctions_ToBsonBinaryDataFromIntWithOptions_should_work(int id, ByteOrder byteOrder, string expectedBase64, bool setOnError, string onErrorBase64, bool setOnNull, string onNullBase64) + { + RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); + + var options = new ConvertOptions(); + if (setOnError) options.OnError = onErrorBase64 == null ? BsonNull.Value : new BsonBinaryData(Convert.FromBase64String(onErrorBase64)); + if (setOnNull) options.OnNull = onNullBase64 == null ? BsonNull.Value : new BsonBinaryData(Convert.FromBase64String(onNullBase64)); + + var collection = Fixture.Collection; + var queryable = collection.AsQueryable() + .Where(x => x.Id == id) + .Select(x => Mql.ToBsonBinaryData(x.IntProperty, BsonBinarySubType.Binary, byteOrder, options)); + + var onErrorStr = setOnError ? $"onError: {FormatBase64(onErrorBase64)}," : ""; + var onNullStr = setOnNull ? $"onNull: {FormatBase64(onNullBase64)}," : ""; + + var expectedStages = + new[] + { + $"{{ $match : {{ _id : {id} }} }}", + $"{{ $project: {{ _v : {{ $convert : {{ input : '$IntProperty', to : {{ type: 'binData', subtype: 0 }}, {onErrorStr} {onNullStr} {ByteOrderToString(byteOrder)}}} }}, _id : 0 }} }}", + }; + + BsonValue expectedResult = expectedBase64 is null? BsonNull.Value : new BsonBinaryData(Convert.FromBase64String(expectedBase64)); + AssertOutcome(collection, queryable, expectedStages, expectedResult); + } + + [Theory] + [InlineData(0, ByteOrder.LittleEndian, "AAAAAAAABMA=", true, "Ag==", true, "AAAAAAAABMA=")] + [InlineData(10, ByteOrder.LittleEndian, "Ag==", true, "Ag==", true, "AAAAAAAABMA=")] + [InlineData(0, ByteOrder.BigEndian, "AAAAAAAABMA=", true, "Ag==", true, "AAAAAAAABMA=")] + [InlineData(10, ByteOrder.BigEndian, "Ag==", true, "Ag==", true, "AAAAAAAABMA=")] + [InlineData(0, ByteOrder.BigEndian, null, true, "Ag==", true, null)] + [InlineData(10, ByteOrder.BigEndian, null, true, null, true, "AAAAAAAABMA=")] + [InlineData(0, ByteOrder.BigEndian, null, false, "Ag==", true, null)] + [InlineData(10, ByteOrder.BigEndian, null, true, null, false, "AAAAAAAABMA=")] + public void MongoDBFunctions_ToBsonBinaryDataFromNullableIntWithOptions_should_work(int id, ByteOrder byteOrder, string expectedBase64, bool setOnError, string onErrorBase64, bool setOnNull, string onNullBase64) + { + RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); + + var options = new ConvertOptions(); + if (setOnError) options.OnError = onErrorBase64 == null ? BsonNull.Value : new BsonBinaryData(Convert.FromBase64String(onErrorBase64)); + if (setOnNull) options.OnNull = onNullBase64 == null ? BsonNull.Value : new BsonBinaryData(Convert.FromBase64String(onNullBase64)); + + var collection = Fixture.Collection; + var queryable = collection.AsQueryable() + .Where(x => x.Id == id) + .Select(x => Mql.ToBsonBinaryData(x.NullableIntProperty, BsonBinarySubType.Binary, byteOrder, options)); + + var onErrorStr = setOnError ? $"onError: {FormatBase64(onErrorBase64)}," : ""; + var onNullStr = setOnNull ? $"onNull: {FormatBase64(onNullBase64)}," : ""; + + var expectedStages = + new[] + { + $"{{ $match : {{ _id : {id} }} }}", + $"{{ $project: {{ _v : {{ $convert : {{ input : '$NullableIntProperty', to : {{ type: 'binData', subtype: 0 }}, {onErrorStr} {onNullStr} {ByteOrderToString(byteOrder)}}} }}, _id : 0 }} }}", + }; + + BsonValue expectedResult = expectedBase64 is null? BsonNull.Value : new BsonBinaryData(Convert.FromBase64String(expectedBase64)); + AssertOutcome(collection, queryable, expectedStages, expectedResult); + } [Theory] [InlineData(4, ByteOrder.LittleEndian,"ogIAAA==", null)] @@ -266,37 +346,79 @@ public void MongoDBFunctions_ToBsonBinaryDataFromNullableLong_should_work(int id AssertOutcome(collection, queryable, expectedStages, expectedResult, expectedException); } - // [Theory] - // [InlineData(0, ByteOrder.LittleEndian, "AAAAAAAABMA=", "Ag==", "AAAAAAAABMA=")] - // [InlineData(10, ByteOrder.LittleEndian, "Ag==", "Ag==", "AAAAAAAABMA=")] - // [InlineData(0, ByteOrder.BigEndian, "AAAAAAAABMA=", "Ag==", "AAAAAAAABMA=")] - // [InlineData(10, ByteOrder.BigEndian, "Ag==", "Ag==", "AAAAAAAABMA=")] - // public void MongoDBFunctions_ToLongDataFromIntWithOnErrorAndOnNull_should_work(int id, ByteOrder byteOrder, string expectedBase64, string onErrorBase64, string onNullBase64) - // { - // RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); - // - // var onErrorBinData = onErrorBase64 == null ? null : new BsonBinaryData(Convert.FromBase64String(onErrorBase64)); - // var onNullBinData = onNullBase64 == null ? null : new BsonBinaryData(Convert.FromBase64String(onNullBase64)); - // - // var collection = Fixture.Collection; - // var queryable = collection.AsQueryable() - // .Where(x => x.Id == id) - // .Select(x => Mql.ToBsonBinaryData(x.LongProperty, BsonBinarySubType.Binary, byteOrder, onErrorBinData, onNullBinData)); - // - // var onErrorString = onErrorBase64 == null ? "null" : $"BinData(0, '{onErrorBase64}')"; - // var onNullString = onNullBase64 == null ? "null" : $"BinData(0, '{onNullBase64}')"; - // var expectedStages = - // new[] - // { - // $"{{ $match : {{ _id : {id} }} }}", - // $"{{ $project: {{ _v : {{ $convert : {{ input : '$LongProperty', to : {{ type: 'binData', subtype: 0 }}, onError: {onErrorString}, onNull: {onNullString}, {ByteOrderToString(byteOrder)}}} }}, _id : 0 }} }}", - // }; - // - // var expectedResult = expectedBase64 is null? null : new BsonBinaryData(Convert.FromBase64String(expectedBase64)); - // AssertOutcome(collection, queryable, expectedStages, expectedResult); - // } + [Theory] + [InlineData(0, ByteOrder.LittleEndian, "AAAAAAAABMA=", true, "AAAAAAAA4L8=", true, "AAAAAAAABMA=")] + [InlineData(10, ByteOrder.LittleEndian, "AAAAAAAA4L8=", true, "AAAAAAAA4L8=", true, "AAAAAAAABMA=")] + [InlineData(0, ByteOrder.BigEndian, "AAAAAAAABMA=", true, "AAAAAAAA4L8=", true, "AAAAAAAABMA=")] + [InlineData(10, ByteOrder.BigEndian, "AAAAAAAA4L8=", true, "AAAAAAAA4L8=", true, "AAAAAAAABMA=")] + [InlineData(0, ByteOrder.BigEndian, null, true, "AAAAAAAA4L8=", true, null)] + [InlineData(10, ByteOrder.BigEndian, null, true, null, true, "AAAAAAAABMA=")] + public void MongoDBFunctions_ToBsonBinaryDataFromLongWithOptions_should_work(int id, ByteOrder byteOrder, string expectedBase64, bool setOnError, string onErrorBase64, bool setOnNull, string onNullBase64) + { + RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); + + var options = new ConvertOptions(); + if (setOnError) options.OnError = onErrorBase64 == null ? BsonNull.Value : new BsonBinaryData(Convert.FromBase64String(onErrorBase64)); + if (setOnNull) options.OnNull = onNullBase64 == null ? BsonNull.Value : new BsonBinaryData(Convert.FromBase64String(onNullBase64)); + + var collection = Fixture.Collection; + var queryable = collection.AsQueryable() + .Where(x => x.Id == id) + .Select(x => Mql.ToBsonBinaryData(x.LongProperty, BsonBinarySubType.Binary, byteOrder, options)); + + var onErrorStr = setOnError ? $"onError: {FormatBase64(onErrorBase64)}," : ""; + var onNullStr = setOnNull ? $"onNull: {FormatBase64(onNullBase64)}," : ""; + + var expectedStages = + new[] + { + $"{{ $match : {{ _id : {id} }} }}", + $"{{ $project: {{ _v : {{ $convert : {{ input : '$LongProperty', to : {{ type: 'binData', subtype: 0 }}, {onErrorStr} {onNullStr} {ByteOrderToString(byteOrder)}}} }}, _id : 0 }} }}", + }; + + BsonValue expectedResult = expectedBase64 is null? BsonNull.Value : new BsonBinaryData(Convert.FromBase64String(expectedBase64)); + AssertOutcome(collection, queryable, expectedStages, expectedResult); + } + + [Theory] + [InlineData(0, ByteOrder.LittleEndian, "AAAAAAAABMA=", true, "AAAAAAAA4L8=", true, "AAAAAAAABMA=")] + [InlineData(10, ByteOrder.LittleEndian, "AAAAAAAA4L8=", true, "AAAAAAAA4L8=", true, "AAAAAAAABMA=")] + [InlineData(0, ByteOrder.BigEndian, "AAAAAAAABMA=", true, "AAAAAAAA4L8=", true, "AAAAAAAABMA=")] + [InlineData(10, ByteOrder.BigEndian, "AAAAAAAA4L8=", true, "AAAAAAAA4L8=", true, "AAAAAAAABMA=")] + [InlineData(0, ByteOrder.BigEndian, null, true, "AAAAAAAA4L8=", true, null)] + [InlineData(10, ByteOrder.BigEndian, null, true, null, true, "AAAAAAAABMA=")] + [InlineData(0, ByteOrder.BigEndian, null, false, "AAAAAAAA4L8=", true, null)] + [InlineData(10, ByteOrder.BigEndian, null, true, null, false, "AAAAAAAABMA=")] + public void MongoDBFunctions_ToBsonBinaryDataFromNullableLongWithOptions_should_work(int id, ByteOrder byteOrder, string expectedBase64, bool setOnError, string onErrorBase64, bool setOnNull, string onNullBase64) + { + RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); + + var options = new ConvertOptions(); + if (setOnError) options.OnError = onErrorBase64 == null ? BsonNull.Value : new BsonBinaryData(Convert.FromBase64String(onErrorBase64)); + if (setOnNull) options.OnNull = onNullBase64 == null ? BsonNull.Value : new BsonBinaryData(Convert.FromBase64String(onNullBase64)); + + var collection = Fixture.Collection; + var queryable = collection.AsQueryable() + .Where(x => x.Id == id) + .Select(x => Mql.ToBsonBinaryData(x.NullableLongProperty, BsonBinarySubType.Binary, byteOrder, options)); + + var onErrorStr = setOnError ? $"onError: {FormatBase64(onErrorBase64)}," : ""; + var onNullStr = setOnNull ? $"onNull: {FormatBase64(onNullBase64)}," : ""; + + var expectedStages = + new[] + { + $"{{ $match : {{ _id : {id} }} }}", + $"{{ $project: {{ _v : {{ $convert : {{ input : '$NullableLongProperty', to : {{ type: 'binData', subtype: 0 }}, {onErrorStr} {onNullStr} {ByteOrderToString(byteOrder)}}} }}, _id : 0 }} }}", + }; + + BsonValue expectedResult = expectedBase64 is null? BsonNull.Value : new BsonBinaryData(Convert.FromBase64String(expectedBase64)); + AssertOutcome(collection, queryable, expectedStages, expectedResult); + } + [Theory] + [InlineData(1, null, null)] [InlineData(2, "867dee52-c331-484e-92d1-c56479b8e67e", null)] [InlineData(10, null, "MongoCommandException")] public void MongoDBFunctions_ToBsonBinaryDataFromString_should_work(int id, string expectedGuidString, string expectedException) @@ -315,38 +437,41 @@ public void MongoDBFunctions_ToBsonBinaryDataFromString_should_work(int id, stri $"{{ $project: {{ _v : {{ $convert : {{ input : '$StringProperty', to : {{ type: 'binData', subtype: 4 }}, format: 'uuid' }} }}, _id : 0 }} }}", }; - var expectedResult = expectedGuidString is null? null : new BsonBinaryData(Guid.Parse(expectedGuidString), GuidRepresentation.Standard); + BsonValue expectedResult = expectedGuidString is null? BsonNull.Value : new BsonBinaryData(Guid.Parse(expectedGuidString), GuidRepresentation.Standard); AssertOutcome(collection, queryable, expectedStages, expectedResult, expectedException); } - // [Theory] - // [InlineData(0, "AAAAAAAABMA=", "Ag==", "AAAAAAAABMA=")] - // [InlineData(10, "Ag==", "Ag==", "AAAAAAAABMA=")] - // public void MongoDBFunctions_ToBsonBinaryDataFromStringWithOnErrorAndOnNull_should_work(int id, string expectedBase64, string onErrorBase64, string onNullBase64) - // { - // RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); - // - // var onErrorBinData = onErrorBase64 == null ? null : new BsonBinaryData(Convert.FromBase64String(onErrorBase64)); - // var onNullBinData = onNullBase64 == null ? null : new BsonBinaryData(Convert.FromBase64String(onNullBase64)); - // - // var collection = Fixture.Collection; - // var queryable = collection.AsQueryable() - // .Where(x => x.Id == id) - // .Select(x => Mql.ToBsonBinaryData(x.StringProperty, BsonBinarySubType.UuidStandard, "uuid", onErrorBinData, onNullBinData)); - // - // var onErrorString = onErrorBase64 == null ? "null" : $"BinData(0, '{onErrorBase64}')"; - // var onNullString = onNullBase64 == null ? "null" : $"BinData(0, '{onNullBase64}')"; - // - // var expectedStages = - // new[] - // { - // $"{{ $match : {{ _id : {id} }} }}", - // $"{{ $project: {{ _v : {{ $convert : {{ input : '$StringProperty', to : {{ type: 'binData', subtype: 4 }}, onError: {onErrorString}, onNull: {onNullString}, format: 'uuid' }} }}, _id : 0 }} }}", - // }; - // - // var expectedResult = expectedBase64 is null? null : new BsonBinaryData(Convert.FromBase64String(expectedBase64)); - // AssertOutcome(collection, queryable, expectedStages, expectedResult); - // } + [Theory] + [InlineData(0, "AAAAAAAABMA=", true, "Ag==", true, "AAAAAAAABMA=")] + [InlineData(10, "Ag==", true, "Ag==", true, "AAAAAAAABMA=")] + [InlineData(0, "AAAAAAAABMA=", false, "Ag==", true, "AAAAAAAABMA=")] + [InlineData(10, "Ag==", true, "Ag==", false, "AAAAAAAABMA=")] + public void MongoDBFunctions_ToBsonBinaryDataFromStringWithOptions_should_work(int id, string expectedBase64, bool setOnError, string onErrorBase64, bool setOnNull, string onNullBase64) + { + RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); + + var options = new ConvertOptions(); + if (setOnError) options.OnError = onErrorBase64 == null ? BsonNull.Value : new BsonBinaryData(Convert.FromBase64String(onErrorBase64)); + if (setOnNull) options.OnNull = onNullBase64 == null ? BsonNull.Value : new BsonBinaryData(Convert.FromBase64String(onNullBase64)); + + var collection = Fixture.Collection; + var queryable = collection.AsQueryable() + .Where(x => x.Id == id) + .Select(x => Mql.ToBsonBinaryData(x.StringProperty, BsonBinarySubType.UuidStandard, "uuid", options)); + + var onErrorStr = setOnError ? $"onError: {FormatBase64(onErrorBase64)}," : ""; + var onNullStr = setOnNull ? $"onNull: {FormatBase64(onNullBase64)}," : ""; + + var expectedStages = + new[] + { + $"{{ $match : {{ _id : {id} }} }}", + $"{{ $project: {{ _v : {{ $convert : {{ input : '$StringProperty', to : {{ type: 'binData', subtype: 4 }}, {onErrorStr} {onNullStr} format: 'uuid' }} }}, _id : 0 }} }}", + }; + + var expectedResult = expectedBase64 is null? null : new BsonBinaryData(Convert.FromBase64String(expectedBase64)); + AssertOutcome(collection, queryable, expectedStages, expectedResult); + } // To Double @@ -405,7 +530,7 @@ public void MongoDBFunctions_ToNullableDoubleFromBsonBinaryData_should_work(int [InlineData(2, ByteOrder.LittleEndian, 0, true, 0, true, 22.3)] [InlineData(0, ByteOrder.LittleEndian, 0, true, 15.2, true, 0)] [InlineData(0, ByteOrder.LittleEndian, 0, false, 15.2, true, 0)] - public void MongoDBFunctions_ToDoubleFromBsonBinaryDataWithOnErrorAndOnNull_should_work(int id, ByteOrder byteOrder, double expectedResult, bool setOnError, double onError, bool setOnNull, double onNull) + public void MongoDBFunctions_ToDoubleFromBsonBinaryDataWithOptions_should_work(int id, ByteOrder byteOrder, double expectedResult, bool setOnError, double onError, bool setOnNull, double onNull) { RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); @@ -440,7 +565,7 @@ public void MongoDBFunctions_ToDoubleFromBsonBinaryDataWithOnErrorAndOnNull_shou [InlineData(0, ByteOrder.LittleEndian, null, true, 15.2, true, null)] [InlineData(2, ByteOrder.LittleEndian, null, true, null, false, 22.3)] [InlineData(0, ByteOrder.LittleEndian, null, false, 15.2, true, null)] - public void MongoDBFunctions_ToNullableDoubleFromBsonBinaryDataWithOnErrorAndOnNull_should_work(int id, ByteOrder byteOrder, double? expectedResult, bool setOnError, double? onError, bool setOnNull, double? onNull) + public void MongoDBFunctions_ToNullableDoubleFromBsonBinaryDataWithOptions_should_work(int id, ByteOrder byteOrder, double? expectedResult, bool setOnError, double? onError, bool setOnNull, double? onNull) { RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); @@ -523,7 +648,7 @@ public void MongoDBFunctions_ToNullableIntFromBsonBinaryData_should_work(int id, [InlineData(2, ByteOrder.LittleEndian, 0, true, 0, true, 22)] [InlineData(0, ByteOrder.LittleEndian, 0, true, 15, true, 0)] [InlineData(0, ByteOrder.LittleEndian, 0, false, 15, true, 0)] - public void MongoDBFunctions_ToIntFromBsonBinaryDataWithOnErrorAndOnNull_should_work(int id, ByteOrder byteOrder, int expectedResult, bool setOnError, int onError, bool setOnNull, int onNull) + public void MongoDBFunctions_ToIntFromBsonBinaryDataWithOptions_should_work(int id, ByteOrder byteOrder, int expectedResult, bool setOnError, int onError, bool setOnNull, int onNull) { RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); @@ -558,7 +683,7 @@ public void MongoDBFunctions_ToIntFromBsonBinaryDataWithOnErrorAndOnNull_should_ [InlineData(0, ByteOrder.LittleEndian, null, true, 15, true, null)] [InlineData(2, ByteOrder.LittleEndian, null, true, null, false, 22)] [InlineData(0, ByteOrder.LittleEndian, null, false, 15, true, null)] - public void MongoDBFunctions_ToNullableIntFromBsonBinaryDataWithOnErrorAndOnNull_should_work(int id, ByteOrder byteOrder, int? expectedResult, bool setOnError, int? onError, bool setOnNull, int? onNull) + public void MongoDBFunctions_ToNullableIntFromBsonBinaryDataWithOptions_should_work(int id, ByteOrder byteOrder, int? expectedResult, bool setOnError, int? onError, bool setOnNull, int? onNull) { RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); @@ -641,7 +766,7 @@ public void MongoDBFunctions_ToNullableLongFromBsonBinaryData_should_work(int id [InlineData(2, ByteOrder.LittleEndian, 0, true, 0, true, (long)22)] [InlineData(0, ByteOrder.LittleEndian, 0, true, (long)15, true, 0)] [InlineData(0, ByteOrder.LittleEndian, 0, false, (long)15, true, 0)] - public void MongoDBFunctions_ToLongFromBsonBinaryDataWithOnErrorAndOnNull_should_work(int id, ByteOrder byteOrder, long expectedResult, bool setOnError, long onError, bool setOnNull, long onNull) + public void MongoDBFunctions_ToLongFromBsonBinaryDataWithOptions_should_work(int id, ByteOrder byteOrder, long expectedResult, bool setOnError, long onError, bool setOnNull, long onNull) { RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); @@ -676,7 +801,7 @@ public void MongoDBFunctions_ToLongFromBsonBinaryDataWithOnErrorAndOnNull_should [InlineData(0, ByteOrder.LittleEndian, null, true, (long)15, true, null)] [InlineData(2, ByteOrder.LittleEndian, null, true, null, false, (long)22)] [InlineData(0, ByteOrder.LittleEndian, null, false, (long)15, true, null)] - public void MongoDBFunctions_ToNullableLongFromBsonBinaryDataWithOnErrorAndOnNull_should_work(int id, ByteOrder byteOrder, long? expectedResult, bool setOnError, long? onError, bool setOnNull, long? onNull) + public void MongoDBFunctions_ToNullableLongFromBsonBinaryDataWithOptions_should_work(int id, ByteOrder byteOrder, long? expectedResult, bool setOnError, long? onError, bool setOnNull, long? onNull) { RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); @@ -733,7 +858,7 @@ public void MongoDBFunctions_ToStringFromBsonBinaryData_should_work(int id, stri [InlineData(1, null, true, null, true, "onNull")] [InlineData(0, null, false, "onError", true, null)] [InlineData(1, null, true, null, false, "onNull")] - public void MongoDBFunctions_ToStringFromBsonBinaryDataWithOnErrorAndOnNull_should_work(int id, string expectedResult, bool setOnError, string onError, bool setOnNull, string onNull) + public void MongoDBFunctions_ToStringFromBsonBinaryDataWithOptions_should_work(int id, string expectedResult, bool setOnError, string onError, bool setOnNull, string onNull) { RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromString); @@ -807,6 +932,9 @@ private static string Format(long? value) => private static string Format(string value) => value is null ? "null" : $"'{value}'"; + private static string FormatBase64(string base64String) => + base64String is null ? "null" :$"BinData(0, '{base64String}')"; + public sealed class ClassFixture : MongoCollectionFixture { protected override IEnumerable InitialData => From d7bca771b228f498e34a8ee3ba4f5b66c4c4872f Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Wed, 26 Mar 2025 16:13:18 +0100 Subject: [PATCH 45/82] Added more tests --- ...dToAggregationExpressionTranslatorTests.cs | 261 ++++++++++++++++++ 1 file changed, 261 insertions(+) diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs index 5bbccb4e5ab..a2246ed92ca 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs @@ -35,6 +35,111 @@ public ConvertMethodToAggregationExpressionTranslatorTests(ClassFixture fixture) // To BinData + [Fact] + public void MongoDBFunctions_ToBsonBinaryDataFromString_when_options_is_null_should_throw() + { + RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); + + var collection = Fixture.Collection; + var queryable = collection.AsQueryable() + .Where(x => x.Id == 1) + .Select(x => Mql.ToBsonBinaryData(x.StringProperty, BsonBinarySubType.Binary, "format", null)); + + var exception = Record.Exception(() => Translate(collection, queryable)); + Assert.IsType(exception); + Assert.Equal("The 'options' argument cannot be null", exception.Message); + } + + [Fact] + public void MongoDBFunctions_ToBsonBinaryDataFromInt_when_options_is_null_should_throw() + { + RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); + + var collection = Fixture.Collection; + var queryable = collection.AsQueryable() + .Where(x => x.Id == 1) + .Select(x => Mql.ToBsonBinaryData(x.IntProperty, BsonBinarySubType.Binary, ByteOrder.BigEndian, null)); + + var exception = Record.Exception(() => Translate(collection, queryable)); + Assert.IsType(exception); + Assert.Equal("The 'options' argument cannot be null", exception.Message); + } + + [Fact] + public void MongoDBFunctions_ToBsonBinaryDataFromNullableInt_when_options_is_null_should_throw() + { + RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); + + var collection = Fixture.Collection; + var queryable = collection.AsQueryable() + .Where(x => x.Id == 1) + .Select(x => Mql.ToBsonBinaryData(x.NullableIntProperty, BsonBinarySubType.Binary, ByteOrder.BigEndian, null)); + + var exception = Record.Exception(() => Translate(collection, queryable)); + Assert.IsType(exception); + Assert.Equal("The 'options' argument cannot be null", exception.Message); + } + + [Fact] + public void MongoDBFunctions_ToBsonBinaryDataFromDouble_when_options_is_null_should_throw() + { + RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); + + var collection = Fixture.Collection; + var queryable = collection.AsQueryable() + .Where(x => x.Id == 1) + .Select(x => Mql.ToBsonBinaryData(x.DoubleProperty, BsonBinarySubType.Binary, ByteOrder.BigEndian, null)); + + var exception = Record.Exception(() => Translate(collection, queryable)); + Assert.IsType(exception); + Assert.Equal("The 'options' argument cannot be null", exception.Message); + } + + [Fact] + public void MongoDBFunctions_ToBsonBinaryDataFromNullableDouble_when_options_is_null_should_throw() + { + RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); + + var collection = Fixture.Collection; + var queryable = collection.AsQueryable() + .Where(x => x.Id == 1) + .Select(x => Mql.ToBsonBinaryData(x.NullableDoubleProperty, BsonBinarySubType.Binary, ByteOrder.BigEndian, null)); + + var exception = Record.Exception(() => Translate(collection, queryable)); + Assert.IsType(exception); + Assert.Equal("The 'options' argument cannot be null", exception.Message); + } + + [Fact] + public void MongoDBFunctions_ToBsonBinaryDataFromLong_when_options_is_null_should_throw() + { + RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); + + var collection = Fixture.Collection; + var queryable = collection.AsQueryable() + .Where(x => x.Id == 1) + .Select(x => Mql.ToBsonBinaryData(x.LongProperty, BsonBinarySubType.Binary, ByteOrder.BigEndian, null)); + + var exception = Record.Exception(() => Translate(collection, queryable)); + Assert.IsType(exception); + Assert.Equal("The 'options' argument cannot be null", exception.Message); + } + + [Fact] + public void MongoDBFunctions_ToBsonBinaryDataFromNullableLong_when_options_is_null_should_throw() + { + RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); + + var collection = Fixture.Collection; + var queryable = collection.AsQueryable() + .Where(x => x.Id == 1) + .Select(x => Mql.ToBsonBinaryData(x.NullableLongProperty, BsonBinarySubType.Binary, ByteOrder.BigEndian, null)); + + var exception = Record.Exception(() => Translate(collection, queryable)); + Assert.IsType(exception); + Assert.Equal("The 'options' argument cannot be null", exception.Message); + } + [Theory] [InlineData(3, ByteOrder.LittleEndian,"AAAAAAAA4L8=", null)] [InlineData(5, ByteOrder.BigEndian, "wAQAAAAAAAA=", null )] @@ -475,6 +580,53 @@ public void MongoDBFunctions_ToBsonBinaryDataFromStringWithOptions_should_work(i // To Double + [Fact] + public void MongoDBFunctions_ToDoubleFromBsonBinaryData_when_onNull_is_not_set_should_throw() + { + RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); + + var options = new ConvertOptions { OnError = 22 }; + + var collection = Fixture.Collection; + var queryable = collection.AsQueryable() + .Where(x => x.Id == 1) + .Select(x => Mql.ToDouble(x.BinaryProperty, ByteOrder.LittleEndian, options)); + + var exception = Record.Exception(() => Translate(collection, queryable)); + Assert.IsType(exception); + Assert.Equal("When converting to a non-nullable type, you need to set 'onNull'", exception.Message); + } + + [Fact] + public void MongoDBFunctions_ToDoubleFromBsonBinaryData_when_options_is_null_should_throw() + { + RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); + + var collection = Fixture.Collection; + var queryable = collection.AsQueryable() + .Where(x => x.Id == 1) + .Select(x => Mql.ToDouble(x.BinaryProperty, ByteOrder.LittleEndian, null)); + + var exception = Record.Exception(() => Translate(collection, queryable)); + Assert.IsType(exception); + Assert.Equal("The 'options' argument cannot be null", exception.Message); + } + + [Fact] + public void MongoDBFunctions_ToNullableDoubleFromBsonBinaryData_when_options_is_null_should_throw() + { + RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); + + var collection = Fixture.Collection; + var queryable = collection.AsQueryable() + .Where(x => x.Id == 1) + .Select(x => Mql.ToNullableDouble(x.BinaryProperty, ByteOrder.LittleEndian, null)); + + var exception = Record.Exception(() => Translate(collection, queryable)); + Assert.IsType(exception); + Assert.Equal("The 'options' argument cannot be null", exception.Message); + } + [Theory] [InlineData(2, ByteOrder.BigEndian, 0, "MongoCommandException")] [InlineData(3, ByteOrder.LittleEndian, -0.5, null)] @@ -593,6 +745,53 @@ public void MongoDBFunctions_ToNullableDoubleFromBsonBinaryDataWithOptions_shoul // To Int + [Fact] + public void MongoDBFunctions_ToIntFromBsonBinaryData_when_onNull_is_not_set_should_throw() + { + RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); + + var options = new ConvertOptions { OnError = 22 }; + + var collection = Fixture.Collection; + var queryable = collection.AsQueryable() + .Where(x => x.Id == 1) + .Select(x => Mql.ToInt(x.BinaryProperty, ByteOrder.LittleEndian, options)); + + var exception = Record.Exception(() => Translate(collection, queryable)); + Assert.IsType(exception); + Assert.Equal("When converting to a non-nullable type, you need to set 'onNull'", exception.Message); + } + + [Fact] + public void MongoDBFunctions_ToIntFromBsonBinaryData_when_options_is_null_should_throw() + { + RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); + + var collection = Fixture.Collection; + var queryable = collection.AsQueryable() + .Where(x => x.Id == 1) + .Select(x => Mql.ToInt(x.BinaryProperty, ByteOrder.LittleEndian, null)); + + var exception = Record.Exception(() => Translate(collection, queryable)); + Assert.IsType(exception); + Assert.Equal("The 'options' argument cannot be null", exception.Message); + } + + [Fact] + public void MongoDBFunctions_ToNullableIntFromBsonBinaryData_when_options_is_null_should_throw() + { + RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); + + var collection = Fixture.Collection; + var queryable = collection.AsQueryable() + .Where(x => x.Id == 1) + .Select(x => Mql.ToNullableInt(x.BinaryProperty, ByteOrder.LittleEndian, null)); + + var exception = Record.Exception(() => Translate(collection, queryable)); + Assert.IsType(exception); + Assert.Equal("The 'options' argument cannot be null", exception.Message); + } + [Theory] [InlineData(2, ByteOrder.LittleEndian, 0, "MongoCommandException")] [InlineData(4, ByteOrder.LittleEndian, 674, null)] @@ -711,6 +910,53 @@ public void MongoDBFunctions_ToNullableIntFromBsonBinaryDataWithOptions_should_w // To Long + [Fact] + public void MongoDBFunctions_ToLongFromBsonBinaryData_when_onNull_is_not_set_should_throw() + { + RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); + + var options = new ConvertOptions { OnError = 22 }; + + var collection = Fixture.Collection; + var queryable = collection.AsQueryable() + .Where(x => x.Id == 1) + .Select(x => Mql.ToLong(x.BinaryProperty, ByteOrder.LittleEndian, options)); + + var exception = Record.Exception(() => Translate(collection, queryable)); + Assert.IsType(exception); + Assert.Equal("When converting to a non-nullable type, you need to set 'onNull'", exception.Message); + } + + [Fact] + public void MongoDBFunctions_ToLongFromBsonBinaryData_when_options_is_null_should_throw() + { + RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); + + var collection = Fixture.Collection; + var queryable = collection.AsQueryable() + .Where(x => x.Id == 1) + .Select(x => Mql.ToLong(x.BinaryProperty, ByteOrder.LittleEndian, null)); + + var exception = Record.Exception(() => Translate(collection, queryable)); + Assert.IsType(exception); + Assert.Equal("The 'options' argument cannot be null", exception.Message); + } + + [Fact] + public void MongoDBFunctions_ToNullableLongFromBsonBinaryData_when_options_is_null_should_throw() + { + RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); + + var collection = Fixture.Collection; + var queryable = collection.AsQueryable() + .Where(x => x.Id == 1) + .Select(x => Mql.ToNullableLong(x.BinaryProperty, ByteOrder.LittleEndian, null)); + + var exception = Record.Exception(() => Translate(collection, queryable)); + Assert.IsType(exception); + Assert.Equal("The 'options' argument cannot be null", exception.Message); + } + [Theory] [InlineData(2, ByteOrder.LittleEndian, 0, "MongoCommandException")] [InlineData(4, ByteOrder.LittleEndian, (long)674, null)] @@ -829,6 +1075,21 @@ public void MongoDBFunctions_ToNullableLongFromBsonBinaryDataWithOptions_should_ // To String + [Fact] + public void MongoDBFunctions_ToStringFromBsonBinaryData_when_options_is_null_should_throw() + { + RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); + + var collection = Fixture.Collection; + var queryable = collection.AsQueryable() + .Where(x => x.Id == 1) + .Select(x => Mql.ToString(x.BinaryProperty, "test", null)); + + var exception = Record.Exception(() => Translate(collection, queryable)); + Assert.IsType(exception); + Assert.Equal("The 'options' argument cannot be null", exception.Message); + } + [Theory] [InlineData(2, "867dee52-c331-484e-92d1-c56479b8e67e", null)] [InlineData(1, null, "MongoCommandException")] From f94891cfa942a5cb8894c5c4603d273a94f80b46 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Wed, 26 Mar 2025 16:19:01 +0100 Subject: [PATCH 46/82] Small corrections --- src/MongoDB.Driver/Mql.cs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/MongoDB.Driver/Mql.cs b/src/MongoDB.Driver/Mql.cs index 99820f7d9f5..12cf7a75de4 100644 --- a/src/MongoDB.Driver/Mql.cs +++ b/src/MongoDB.Driver/Mql.cs @@ -526,7 +526,7 @@ public enum ByteOrder } /// - /// + /// Represents the options parameter for the conversion methods in the Mql static class. /// public abstract class ConvertOptions { @@ -539,11 +539,10 @@ public abstract class ConvertOptions internal abstract BsonValue RenderOnNull(); } - //TODO Add docs /// - /// + /// Represents the typed options parameter for the conversion methods in the Mql static class. /// - /// + /// The type of onError and onNull. public class ConvertOptions : ConvertOptions { private TResult _onError; @@ -552,7 +551,7 @@ public class ConvertOptions : ConvertOptions private bool _onNullWasSet; /// - /// + /// The onError parameter. /// public TResult OnError { @@ -565,7 +564,7 @@ public TResult OnError } /// - /// + /// The onNull parameter. /// public TResult OnNull { @@ -580,7 +579,7 @@ public TResult OnNull internal override bool OnErrorWasSet => _onErrorWasSet; internal override bool OnNullWasSet => _onNullWasSet; - internal override BsonValue RenderOnError() //TODO Maybe I don't need to do this here... + internal override BsonValue RenderOnError() { return BsonValue.Create(_onError); } From 0664eb10dab9a6884a170a6c4c486db06d6773ff Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Wed, 26 Mar 2025 16:20:16 +0100 Subject: [PATCH 47/82] Small fix --- src/MongoDB.Driver/Mql.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/MongoDB.Driver/Mql.cs b/src/MongoDB.Driver/Mql.cs index 12cf7a75de4..7e393b73b2d 100644 --- a/src/MongoDB.Driver/Mql.cs +++ b/src/MongoDB.Driver/Mql.cs @@ -541,8 +541,9 @@ public abstract class ConvertOptions /// /// Represents the typed options parameter for the conversion methods in the Mql static class. + /// This class allows to set 'onError' and 'onNull'. /// - /// The type of onError and onNull. + /// The type of 'onError' and 'onNull'. public class ConvertOptions : ConvertOptions { private TResult _onError; From e4c185bde9df024a0c752d3eb21d8bf9efc93e9c Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Wed, 26 Mar 2025 17:21:18 +0100 Subject: [PATCH 48/82] Corrected name --- src/MongoDB.Driver/Core/Misc/Feature.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/MongoDB.Driver/Core/Misc/Feature.cs b/src/MongoDB.Driver/Core/Misc/Feature.cs index c8785f2e027..0a59eb0bf66 100644 --- a/src/MongoDB.Driver/Core/Misc/Feature.cs +++ b/src/MongoDB.Driver/Core/Misc/Feature.cs @@ -44,8 +44,8 @@ public class Feature private static readonly Feature __clientBulkWrite = new Feature("ClientBulkWrite", WireVersion.Server80); private static readonly Feature __clientSideEncryption = new Feature("ClientSideEncryption", WireVersion.Server42); private static readonly Feature __clusteredIndexes = new Feature("ClusteredIndexes", WireVersion.Server53); - private static readonly Feature __convertOperatorBinDataToFromNumeric = new Feature("ConvertBinDataToFromNumeric", WireVersion.Server81); - private static readonly Feature __convertOperatorBinDataToFromString= new Feature("ConvertBinDataToFromString", WireVersion.Server80); + private static readonly Feature __convertOperatorBinDataToFromNumeric = new Feature("ConvertOperatorBinDataToFromNumeric", WireVersion.Server81); + private static readonly Feature __convertOperatorBinDataToFromString= new Feature("ConvertOperatorBinDataToFromString", WireVersion.Server80); private static readonly Feature __createIndexCommitQuorum = new Feature("CreateIndexCommitQuorum", WireVersion.Server44); private static readonly Feature __createIndexesUsingInsertOperations = new Feature("CreateIndexesUsingInsertOperations", WireVersion.Zero, WireVersion.Server42); private static readonly Feature __csfleRangeAlgorithm = new Feature("CsfleRangeAlgorithm", WireVersion.Server62); From 3af159e424b9204e8ca67db6917dc04133490d43 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Wed, 26 Mar 2025 17:47:57 +0100 Subject: [PATCH 49/82] Various fixes according to PR --- .../Ast/Expressions/AstConvertExpression.cs | 24 +++++++----- .../Ast/Expressions/AstExpression.cs | 7 ++-- .../Ast/Visitors/AstNodeVisitor.cs | 2 +- ...MethodToAggregationExpressionTranslator.cs | 14 ++++++- src/MongoDB.Driver/Mql.cs | 15 +++----- .../Ast/Expressions/AstExpressionTests.cs | 38 ++++++++----------- 6 files changed, 53 insertions(+), 47 deletions(-) diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstConvertExpression.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstConvertExpression.cs index abfa937939b..14b73f5ea6e 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstConvertExpression.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstConvertExpression.cs @@ -24,7 +24,8 @@ internal sealed class AstConvertExpression : AstExpression private readonly ByteOrder? _byteOrder; private readonly AstExpression _input; private readonly string _format; - private readonly ConvertOptions _options; + private readonly AstExpression _onError; + private readonly AstExpression _onNull; private readonly BsonBinarySubType? _subType; private readonly AstExpression _to; @@ -34,21 +35,24 @@ public AstConvertExpression( BsonBinarySubType? subType = null, ByteOrder? byteOrder = null, string format = null, - ConvertOptions options = null) + AstExpression onError = null, + AstExpression onNull = null) { _input = Ensure.IsNotNull(input, nameof(input)); _to = Ensure.IsNotNull(to, nameof(to)); _subType = subType; _byteOrder = byteOrder; _format = format; - _options = options; + _onError = onError; + _onNull = onNull; } public ByteOrder? ByteOrder => _byteOrder; public AstExpression Input => _input; public string Format => _format; public override AstNodeType NodeType => AstNodeType.ConvertExpression; - public ConvertOptions Options => _options; + public AstExpression OnError => _onError; + public AstExpression OnNull => _onNull; public BsonBinarySubType? SubType => _subType; public AstExpression To => _to; @@ -71,8 +75,8 @@ public override BsonValue Render() {"subtype", (int)_subType!.Value}, }, _subType != null }, - { "onError", () => _options.RenderOnError(), _options?.OnErrorWasSet == true }, - { "onNull", () => _options.RenderOnNull(), _options?.OnNullWasSet == true }, + { "onError", () => _onError.Render(), _onError != null }, + { "onNull", () => _onNull.Render(), _onNull != null }, { "format", () => _format, _format != null}, { "byteOrder", () => _byteOrder!.Value.Render(), _byteOrder != null} } @@ -82,14 +86,16 @@ public override BsonValue Render() public AstConvertExpression Update( AstExpression input, - AstExpression to) + AstExpression to, + AstExpression onError, + AstExpression onNull) { - if (input == _input && to == _to) + if (input == _input && to == _to && onError == _onError && onNull == _onNull) { return this; } - return new AstConvertExpression(input, to, _subType, _byteOrder, _format, _options); + return new AstConvertExpression(input, to, _subType, _byteOrder, _format, onError, onNull); } } } diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstExpression.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstExpression.cs index 5de49b28f0e..8de612f95f1 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstExpression.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstExpression.cs @@ -261,12 +261,13 @@ public static AstExpression Convert( BsonBinarySubType? subType = null, ByteOrder? byteOrder = null, string format = null, - ConvertOptions options = null) + AstExpression onError = null, + AstExpression onNull = null) { Ensure.IsNotNull(input, nameof(input)); Ensure.IsNotNull(to, nameof(to)); - if (options == null && subType == null && format == null && byteOrder == null && + if (onError == null && onNull == null && subType == null && format == null && byteOrder == null && to is AstConstantExpression toConstantExpression && (toConstantExpression.Value as BsonString)?.Value is { } toValue) { @@ -289,7 +290,7 @@ to is AstConstantExpression toConstantExpression && } } - return new AstConvertExpression(input, to, subType, byteOrder, format, options); + return new AstConvertExpression(input, to, subType, byteOrder, format, onError, onNull); } public static AstExpression DateAdd( diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Visitors/AstNodeVisitor.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Visitors/AstNodeVisitor.cs index 4b807f759ba..1222d0060e9 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Visitors/AstNodeVisitor.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Visitors/AstNodeVisitor.cs @@ -226,7 +226,7 @@ public virtual AstNode VisitConstantExpression(AstConstantExpression node) public virtual AstNode VisitConvertExpression(AstConvertExpression node) { - return node.Update(VisitAndConvert(node.Input), VisitAndConvert(node.To)); + return node.Update(VisitAndConvert(node.Input), VisitAndConvert(node.To), VisitAndConvert(node.OnError), VisitAndConvert(node.OnNull)); } public virtual AstNode VisitCountStage(AstCountStage node) diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs index 57960ebe26a..916c341cafa 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs @@ -65,6 +65,8 @@ public static TranslatedExpression Translate(TranslationContext context, MethodC BsonBinarySubType? subType = null; string format = null; ConvertOptions options = null; + AstExpression onErrorAst = null; + AstExpression onNullAst = null; var fieldAst = ExpressionToAggregationExpressionTranslator.Translate(context, arguments[0]).Ast; @@ -119,6 +121,16 @@ public static TranslatedExpression Translate(TranslationContext context, MethodC { throw new InvalidOperationException("When converting to a non-nullable type, you need to set 'onNull'"); } + + if (options.OnErrorWasSet) + { + onErrorAst = options.GetOnError(); + } + + if (options.OnNullWasSet) + { + onNullAst = options.GetOnNull(); + } } else { @@ -126,7 +138,7 @@ public static TranslatedExpression Translate(TranslationContext context, MethodC } } - var ast = AstExpression.Convert(fieldAst, mapping.Type.Render(), subType: subType, byteOrder: byteOrder, format: format, options: options); + var ast = AstExpression.Convert(fieldAst, mapping.Type.Render(), subType: subType, byteOrder: byteOrder, format: format, onError: onErrorAst, onNull: onNullAst); return new TranslatedExpression(expression, ast, mapping.Serializer); } } diff --git a/src/MongoDB.Driver/Mql.cs b/src/MongoDB.Driver/Mql.cs index 7e393b73b2d..65cdcb191c9 100644 --- a/src/MongoDB.Driver/Mql.cs +++ b/src/MongoDB.Driver/Mql.cs @@ -534,9 +534,9 @@ public abstract class ConvertOptions internal abstract bool OnNullWasSet { get; } - internal abstract BsonValue RenderOnError(); + internal abstract BsonValue GetOnError(); - internal abstract BsonValue RenderOnNull(); + internal abstract BsonValue GetOnNull(); } /// @@ -580,14 +580,9 @@ public TResult OnNull internal override bool OnErrorWasSet => _onErrorWasSet; internal override bool OnNullWasSet => _onNullWasSet; - internal override BsonValue RenderOnError() - { - return BsonValue.Create(_onError); - } + internal override BsonValue GetOnError() => BsonValue.Create(_onError); + + internal override BsonValue GetOnNull() => BsonValue.Create(_onNull); - internal override BsonValue RenderOnNull() - { - return BsonValue.Create(_onNull); - } } } diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Ast/Expressions/AstExpressionTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Ast/Expressions/AstExpressionTests.cs index 067b943e634..0f313e27f50 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Ast/Expressions/AstExpressionTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Ast/Expressions/AstExpressionTests.cs @@ -36,7 +36,7 @@ public void Convert_with_to_constant_should_return_short_form_when_possible(stri var input = AstExpression.Constant(BsonNull.Value); var to = AstExpression.Constant(toValue); - var result = AstExpression.Convert(input, to); + var result = AstExpression.Convert(input, to, onError: null, onNull: null); var unaryExpression = result.Should().BeOfType().Subject; unaryExpression.Operator.Should().Be((AstUnaryOperator)expectedOperator); @@ -50,12 +50,13 @@ public void Convert_with_to_constant_should_return_long_form_when_necessary(stri var input = AstExpression.Constant(BsonNull.Value); var to = AstExpression.Constant(toValue); - var result = AstExpression.Convert(input, to); + var result = AstExpression.Convert(input, to, onError: null, onNull: null); var convertExpression = result.Should().BeOfType().Subject; convertExpression.Input.Should().BeSameAs(input); convertExpression.To.Should().BeSameAs(to); - convertExpression.Options.Should().BeNull(); + convertExpression.OnError.Should().BeNull(); + convertExpression.OnNull.Should().BeNull(); } [Theory] @@ -72,12 +73,13 @@ public void Convert_with_to_expression_should_return_long_form(string toValue) var input = AstExpression.Constant(BsonNull.Value); var to = AstExpression.FieldPath("$To"); - var result = AstExpression.Convert(input, to); + var result = AstExpression.Convert(input, to, onError: null, onNull: null); var convertExpression = result.Should().BeOfType().Subject; convertExpression.Input.Should().BeSameAs(input); convertExpression.To.Should().BeSameAs(to); - convertExpression.Options.Should().BeNull(); + convertExpression.OnError.Should().BeNull(); + convertExpression.OnNull.Should().BeNull(); } [Theory] @@ -93,20 +95,15 @@ public void Convert_with_on_error_should_return_long_form(string toValue) { var input = AstExpression.Constant(BsonNull.Value); var to = AstExpression.Constant(toValue); - var options = new ConvertOptions { OnError = BsonNull.Value }; + var onError = AstExpression.Constant(BsonNull.Value); - var result = AstExpression.Convert(input, to, options: options); + var result = AstExpression.Convert(input, to, onError: onError, onNull: null); var convertExpression = result.Should().BeOfType().Subject; convertExpression.Input.Should().BeSameAs(input); convertExpression.To.Should().BeSameAs(to); - - convertExpression.Options.Should().BeOfType>(); - var retrievedOptions = (ConvertOptions)convertExpression.Options; - retrievedOptions.OnErrorWasSet.Should().BeTrue(); - retrievedOptions.OnError.Should().BeSameAs(options.OnError); - retrievedOptions.OnNullWasSet.Should().BeFalse(); - retrievedOptions.OnNull.Should().BeSameAs(null); + convertExpression.OnError.Should().BeSameAs(onError); + convertExpression.OnNull.Should().BeNull(); } [Theory] @@ -122,20 +119,15 @@ public void Convert_with_on_null_should_return_long_form(string toValue) { var input = AstExpression.Constant(BsonNull.Value); var to = AstExpression.Constant(toValue); - var options = new ConvertOptions { OnNull = BsonNull.Value }; + var onNull = AstExpression.Constant(BsonNull.Value); - var result = AstExpression.Convert(input, to, options: options); + var result = AstExpression.Convert(input, to, onError: null, onNull: onNull); var convertExpression = result.Should().BeOfType().Subject; convertExpression.Input.Should().BeSameAs(input); convertExpression.To.Should().BeSameAs(to); - - convertExpression.Options.Should().BeOfType>(); - var retrievedOptions = (ConvertOptions)convertExpression.Options; - retrievedOptions.OnErrorWasSet.Should().BeFalse(); - retrievedOptions.OnError.Should().BeSameAs(null); - retrievedOptions.OnNullWasSet.Should().BeTrue(); - retrievedOptions.OnNull.Should().BeSameAs(options.OnNull); + convertExpression.OnError.Should().BeNull(); + convertExpression.OnNull.Should().BeSameAs(onNull); } [Fact] From 23ddc747ea9fbcfd2b8d7b367f3632a48d902899 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Tue, 1 Apr 2025 13:59:33 +0200 Subject: [PATCH 50/82] Moving to a simple convert method --- .../Reflection/MqlMethod.cs | 101 +- ...essionToAggregationExpressionTranslator.cs | 8 +- ...MethodToAggregationExpressionTranslator.cs | 148 +-- ...MethodToAggregationExpressionTranslator.cs | 5 - src/MongoDB.Driver/Mql.cs | 386 +----- ...dToAggregationExpressionTranslatorTests.cs | 1091 +---------------- 6 files changed, 88 insertions(+), 1651 deletions(-) diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/MqlMethod.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/MqlMethod.cs index 7a64f176d1e..a5bdcaa6718 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/MqlMethod.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/MqlMethod.cs @@ -23,6 +23,7 @@ namespace MongoDB.Driver.Linq.Linq3Implementation.Reflection internal static class MqlMethod { // private static fields + private static readonly MethodInfo __convert; private static readonly MethodInfo __constantWithRepresentation; private static readonly MethodInfo __constantWithSerializer; private static readonly MethodInfo __dateFromString; @@ -34,38 +35,10 @@ internal static class MqlMethod private static readonly MethodInfo __isMissing; private static readonly MethodInfo __isNullOrMissing; - private static readonly MethodInfo __toBinDataFromDouble; - private static readonly MethodInfo __toBinDataFromDoubleWithOptions; - private static readonly MethodInfo __toBinDataFromInt; - private static readonly MethodInfo __toBinDataFromIntWithOptions; - private static readonly MethodInfo __toBinDataFromLong; - private static readonly MethodInfo __toBinDataFromLongWithOptions; - private static readonly MethodInfo __toBinDataFromNullableDouble; - private static readonly MethodInfo __toBinDataFromNullableDoubleWithOptions; - private static readonly MethodInfo __toBinDataFromNullableInt; - private static readonly MethodInfo __toBinDataFromNullableIntWithOptions; - private static readonly MethodInfo __toBinDataFromNullableLong; - private static readonly MethodInfo __toBinDataFromNullableLongWithOptions; - private static readonly MethodInfo __toBinDataFromString; - private static readonly MethodInfo __toDoubleFromBinData; - private static readonly MethodInfo __toDoubleFromBinDataWithOptions; - private static readonly MethodInfo __toIntFromBinData; - private static readonly MethodInfo __toIntFromBinDataWithOptions; - private static readonly MethodInfo __toLongFromBinData; - private static readonly MethodInfo __toLongFromBinDataWithOptions; - private static readonly MethodInfo __toBinDataFromStringWithOptions; - private static readonly MethodInfo __toNullableDoubleFromBinData; - private static readonly MethodInfo __toNullableDoubleFromBinDataWithOptions; - private static readonly MethodInfo __toNullableIntFromBinData; - private static readonly MethodInfo __toNullableIntFromBinDataWithOptions; - private static readonly MethodInfo __toNullableLongFromBinData; - private static readonly MethodInfo __toNullableLongFromBinDataWithOptions; - private static readonly MethodInfo __toStringFromBinData; - private static readonly MethodInfo __toStringFromBinDataWithOptions; - // static constructor static MqlMethod() { + __convert = ReflectionInfo.Method((object value, ConvertOptions options) => Mql.Convert(value, options)); __constantWithRepresentation = ReflectionInfo.Method((object value, BsonType representation) => Mql.Constant(value, representation)); __constantWithSerializer = ReflectionInfo.Method((object value, IBsonSerializer serializer) => Mql.Constant(value, serializer)); __dateFromString = ReflectionInfo.Method((string dateStringl) => Mql.DateFromString(dateStringl)); @@ -76,50 +49,10 @@ static MqlMethod() __field = ReflectionInfo.Method((object container, string fieldName, IBsonSerializer serializer) => Mql.Field(container, fieldName, serializer)); __isMissing = ReflectionInfo.Method((object field) => Mql.IsMissing(field)); __isNullOrMissing = ReflectionInfo.Method((object field) => Mql.IsNullOrMissing(field)); - - // Convert methods - - __toBinDataFromDouble = ReflectionInfo.Method((double field, BsonBinarySubType subType, ByteOrder byteOrder) => Mql.ToBsonBinaryData(field, subType, byteOrder)); - __toBinDataFromDoubleWithOptions = ReflectionInfo.Method((double field, BsonBinarySubType subType, ByteOrder byteOrder, ConvertOptions options) - => Mql.ToBsonBinaryData(field, subType, byteOrder, options)); - __toBinDataFromInt = ReflectionInfo.Method((int field, BsonBinarySubType subType, ByteOrder byteOrder) => Mql.ToBsonBinaryData(field, subType, byteOrder)); - __toBinDataFromIntWithOptions = ReflectionInfo.Method((int field, BsonBinarySubType subType, ByteOrder byteOrder, ConvertOptions options) - => Mql.ToBsonBinaryData(field, subType, byteOrder, options)); - __toBinDataFromLong = ReflectionInfo.Method((long field, BsonBinarySubType subType, ByteOrder byteOrder) => Mql.ToBsonBinaryData(field, subType, byteOrder)); - __toBinDataFromLongWithOptions = ReflectionInfo.Method((long field, BsonBinarySubType subType, ByteOrder byteOrder, ConvertOptions options) - => Mql.ToBsonBinaryData(field, subType, byteOrder, options)); - - __toBinDataFromNullableDouble = ReflectionInfo.Method((double? field, BsonBinarySubType subType, ByteOrder byteOrder) => Mql.ToBsonBinaryData(field, subType, byteOrder)); - __toBinDataFromNullableDoubleWithOptions = ReflectionInfo.Method((double? field, BsonBinarySubType subType, ByteOrder byteOrder, ConvertOptions options) - => Mql.ToBsonBinaryData(field, subType, byteOrder, options)); - __toBinDataFromNullableInt = ReflectionInfo.Method((int? field, BsonBinarySubType subType, ByteOrder byteOrder) => Mql.ToBsonBinaryData(field, subType, byteOrder)); - __toBinDataFromNullableIntWithOptions = ReflectionInfo.Method((int? field, BsonBinarySubType subType, ByteOrder byteOrder, ConvertOptions options) - => Mql.ToBsonBinaryData(field, subType, byteOrder, options)); - __toBinDataFromNullableLong = ReflectionInfo.Method((long? field, BsonBinarySubType subType, ByteOrder byteOrder) => Mql.ToBsonBinaryData(field, subType, byteOrder)); - __toBinDataFromNullableLongWithOptions = ReflectionInfo.Method((long? field, BsonBinarySubType subType, ByteOrder byteOrder, ConvertOptions options) - => Mql.ToBsonBinaryData(field, subType, byteOrder, options)); - __toBinDataFromString = ReflectionInfo.Method((string field, BsonBinarySubType subType, string format) => Mql.ToBsonBinaryData(field, subType, format)); - __toBinDataFromStringWithOptions = ReflectionInfo.Method((string field, BsonBinarySubType subType, string format, ConvertOptions options) - => Mql.ToBsonBinaryData(field, subType, format, options)); - - __toDoubleFromBinData = ReflectionInfo.Method((BsonBinaryData field, ByteOrder byteOrder) => Mql.ToDouble(field, byteOrder)); - __toDoubleFromBinDataWithOptions = ReflectionInfo.Method((BsonBinaryData field, ByteOrder byteOrder, ConvertOptions options) => Mql.ToDouble(field, byteOrder, options)); - __toIntFromBinData = ReflectionInfo.Method((BsonBinaryData field, ByteOrder byteOrder) => Mql.ToInt(field, byteOrder)); - __toIntFromBinDataWithOptions = ReflectionInfo.Method((BsonBinaryData field, ByteOrder byteOrder, ConvertOptions options) => Mql.ToInt(field, byteOrder, options)); - __toLongFromBinData = ReflectionInfo.Method((BsonBinaryData field, ByteOrder byteOrder) => Mql.ToLong(field, byteOrder)); - __toLongFromBinDataWithOptions = ReflectionInfo.Method((BsonBinaryData field, ByteOrder byteOrder, ConvertOptions options) => Mql.ToLong(field, byteOrder, options)); - - __toNullableDoubleFromBinData = ReflectionInfo.Method((BsonBinaryData field, ByteOrder byteOrder) => Mql.ToNullableDouble(field, byteOrder)); - __toNullableDoubleFromBinDataWithOptions = ReflectionInfo.Method((BsonBinaryData field, ByteOrder byteOrder, ConvertOptions options) => Mql.ToNullableDouble(field, byteOrder, options)); - __toNullableIntFromBinData = ReflectionInfo.Method((BsonBinaryData field, ByteOrder byteOrder) => Mql.ToNullableInt(field, byteOrder)); - __toNullableIntFromBinDataWithOptions = ReflectionInfo.Method((BsonBinaryData field, ByteOrder byteOrder, ConvertOptions options) => Mql.ToNullableInt(field, byteOrder, options)); - __toNullableLongFromBinData = ReflectionInfo.Method((BsonBinaryData field, ByteOrder byteOrder) => Mql.ToNullableLong(field, byteOrder)); - __toNullableLongFromBinDataWithOptions = ReflectionInfo.Method((BsonBinaryData field, ByteOrder byteOrder, ConvertOptions options) => Mql.ToNullableLong(field, byteOrder, options)); - __toStringFromBinData = ReflectionInfo.Method((BsonBinaryData field, string format) => Mql.ToString(field, format)); - __toStringFromBinDataWithOptions = ReflectionInfo.Method((BsonBinaryData field, string format, ConvertOptions options) => Mql.ToString(field, format, options)); } // public properties + public static MethodInfo Convert => __convert; public static MethodInfo ConstantWithRepresentation => __constantWithRepresentation; public static MethodInfo ConstantWithSerializer => __constantWithSerializer; public static MethodInfo DateFromString => __dateFromString; @@ -130,33 +63,5 @@ static MqlMethod() public static MethodInfo Field => __field; public static MethodInfo IsMissing => __isMissing; public static MethodInfo IsNullOrMissing => __isNullOrMissing; - public static MethodInfo ToBinDataFromDouble => __toBinDataFromDouble; - public static MethodInfo ToBinDataFromDoubleWithOptions => __toBinDataFromDoubleWithOptions; - public static MethodInfo ToBinDataFromInt => __toBinDataFromInt; - public static MethodInfo ToBinDataFromIntWithOptions => __toBinDataFromIntWithOptions; - public static MethodInfo ToBinDataFromLong => __toBinDataFromLong; - public static MethodInfo ToBinDataFromLongWithOptions => __toBinDataFromLongWithOptions; - public static MethodInfo ToBinDataFromNullableDouble => __toBinDataFromNullableDouble; - public static MethodInfo ToBinDataFromNullableDoubleWithOptions => __toBinDataFromNullableDoubleWithOptions; - public static MethodInfo ToBinDataFromNullableInt => __toBinDataFromNullableInt; - public static MethodInfo ToBinDataFromNullableIntWithOptions => __toBinDataFromNullableIntWithOptions; - public static MethodInfo ToBinDataFromNullableLong => __toBinDataFromNullableLong; - public static MethodInfo ToBinDataFromNullableLongWithOptions => __toBinDataFromNullableLongWithOptions; - public static MethodInfo ToBinDataFromString => __toBinDataFromString; - public static MethodInfo ToDoubleFromBinData => __toDoubleFromBinData; - public static MethodInfo ToDoubleFromBinDataWithOptions => __toDoubleFromBinDataWithOptions; - public static MethodInfo ToIntFromBinData => __toIntFromBinData; - public static MethodInfo ToIntFromBinDataWithOptions => __toIntFromBinDataWithOptions; - public static MethodInfo ToLongFromBinData => __toLongFromBinData; - public static MethodInfo ToLongFromBinDataWithOptions => __toLongFromBinDataWithOptions; - public static MethodInfo ToBinDataFromStringWithOptions => __toBinDataFromStringWithOptions; - public static MethodInfo ToNullableDoubleFromBinData => __toNullableDoubleFromBinData; - public static MethodInfo ToNullableDoubleFromBinDataWithOptions => __toNullableDoubleFromBinDataWithOptions; - public static MethodInfo ToNullableIntFromBinData => __toNullableIntFromBinData; - public static MethodInfo ToNullableIntFromBinDataWithOptions => __toNullableIntFromBinDataWithOptions; - public static MethodInfo ToNullableLongFromBinData => __toNullableLongFromBinData; - public static MethodInfo ToNullableLongFromBinDataWithOptions => __toNullableLongFromBinDataWithOptions; - public static MethodInfo ToStringFromBinData => __toStringFromBinData; - public static MethodInfo ToStringFromBinDataWithOptions => __toStringFromBinDataWithOptions; } } diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodCallExpressionToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodCallExpressionToAggregationExpressionTranslator.cs index 8b84588ea8c..c052a6c9401 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodCallExpressionToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodCallExpressionToAggregationExpressionTranslator.cs @@ -138,13 +138,7 @@ public static TranslatedExpression Translate(TranslationContext context, MethodC case "LongCount": return CountMethodToAggregationExpressionTranslator.Translate(context, expression); - case "ToBsonBinaryData": - case "ToDouble": - case "ToInt": - case "ToLong": - case "ToNullableDouble": - case "ToNullableInt": - case "ToNullableLong": + case "Convert": return ConvertMethodToAggregationExpressionTranslator.Translate(context, expression); case "ElementAt": diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs index 916c341cafa..0358b59a9f4 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs @@ -8,138 +8,78 @@ using MongoDB.Bson.Serialization.Serializers; using MongoDB.Driver.Linq.Linq3Implementation.Ast; using MongoDB.Driver.Linq.Linq3Implementation.Ast.Expressions; +using MongoDB.Driver.Linq.Linq3Implementation.Misc; using MongoDB.Driver.Linq.Linq3Implementation.Reflection; namespace MongoDB.Driver.Linq.Linq3Implementation.Translators.ExpressionToAggregationExpressionTranslators.MethodTranslators { internal class ConvertMethodToAggregationExpressionTranslator { - private static readonly List<(MethodInfo[] Methods, IBsonSerializer Serializer, BsonType Type, int? FormatIndex, int? SubTypeIndex, int? ByteOrderIndex, int? OptionsIndex)> _methodMappings = - [ - ([MqlMethod.ToBinDataFromString], BsonValueSerializer.Instance, BsonType.Binary, 2, 1, null, null), - ([MqlMethod.ToBinDataFromInt, MqlMethod.ToBinDataFromLong, MqlMethod.ToBinDataFromDouble, MqlMethod.ToBinDataFromNullableInt, MqlMethod.ToBinDataFromNullableLong, MqlMethod.ToBinDataFromNullableDouble], - BsonValueSerializer.Instance, BsonType.Binary, null, 1, 2, null), - ([MqlMethod.ToBinDataFromStringWithOptions], BsonValueSerializer.Instance, BsonType.Binary, 2, 1, null, 3), - ([MqlMethod.ToBinDataFromIntWithOptions, MqlMethod.ToBinDataFromLongWithOptions, MqlMethod.ToBinDataFromDoubleWithOptions, - MqlMethod.ToBinDataFromNullableIntWithOptions, MqlMethod.ToBinDataFromNullableLongWithOptions, MqlMethod.ToBinDataFromNullableDoubleWithOptions], - BsonValueSerializer.Instance, BsonType.Binary, null, 1, 2, 3), - - ([MqlMethod.ToDoubleFromBinData], DoubleSerializer.Instance, BsonType.Double, null, null, 1, null), - ([MqlMethod.ToDoubleFromBinDataWithOptions], DoubleSerializer.Instance, BsonType.Double, null, null, 1, 2), - ([MqlMethod.ToIntFromBinData], Int32Serializer.Instance, BsonType.Int32, null, null, 1, null), - ([MqlMethod.ToIntFromBinDataWithOptions], Int32Serializer.Instance, BsonType.Int32, null, null, 1, 2), - ([MqlMethod.ToLongFromBinData], Int64Serializer.Instance, BsonType.Int64, null, null, 1, null), - ([MqlMethod.ToLongFromBinDataWithOptions], Int64Serializer.Instance, BsonType.Int64, null, null, 1, 2), - - ([MqlMethod.ToNullableDoubleFromBinData], new NullableSerializer(DoubleSerializer.Instance), BsonType.Double, null, null, 1, null), - ([MqlMethod.ToNullableDoubleFromBinDataWithOptions], new NullableSerializer(DoubleSerializer.Instance), BsonType.Double, null, null, 1, 2), - ([MqlMethod.ToNullableIntFromBinData], new NullableSerializer(Int32Serializer.Instance), BsonType.Int32, null, null, 1, null), - ([MqlMethod.ToNullableIntFromBinDataWithOptions], new NullableSerializer(Int32Serializer.Instance), BsonType.Int32, null, null, 1, 2), - ([MqlMethod.ToNullableLongFromBinData], new NullableSerializer(Int64Serializer.Instance), BsonType.Int64, null, null, 1, null), - ([MqlMethod.ToNullableLongFromBinDataWithOptions], new NullableSerializer(Int64Serializer.Instance), BsonType.Int64, null, null, 1, 2), - ([MqlMethod.ToStringFromBinData], StringSerializer.Instance, BsonType.String, 1, null, null, null), - ([MqlMethod.ToStringFromBinDataWithOptions], StringSerializer.Instance, BsonType.String, 1, null, null, 2), - ]; - - private static readonly List ToStringMethods = - [MqlMethod.ToStringFromBinData, MqlMethod.ToStringFromBinDataWithOptions]; - - private static readonly List ToNumericalMethodsWithOptions = - [MqlMethod.ToIntFromBinDataWithOptions, MqlMethod.ToLongFromBinDataWithOptions, MqlMethod.ToDoubleFromBinDataWithOptions]; - - public static bool IsConvertToStringMethod(MethodInfo method) - { - return ToStringMethods.Contains(method); - } - public static TranslatedExpression Translate(TranslationContext context, MethodCallExpression expression) { var method = expression.Method; var arguments = expression.Arguments; - - var mapping = _methodMappings.FirstOrDefault(m => m.Methods.Contains(method)); - if (mapping == default) + + if (!method.Is(MqlMethod.Convert)) + { throw new ExpressionNotSupportedException(expression); + } - ByteOrder? byteOrder = null; - BsonBinarySubType? subType = null; - string format = null; - ConvertOptions options = null; AstExpression onErrorAst = null; AstExpression onNullAst = null; var fieldAst = ExpressionToAggregationExpressionTranslator.Translate(context, arguments[0]).Ast; - if (mapping.ByteOrderIndex.HasValue) + if (arguments[1] is not ConstantExpression constantExpression) { - if (arguments[mapping.ByteOrderIndex.Value] is ConstantExpression co) - { - byteOrder = (ByteOrder)co.Value!; - } - else - { - throw new InvalidOperationException("The 'byteOrder' argument must be a constant expression"); - } + throw new InvalidOperationException("The 'options' argument must be a constant expression"); } - if (mapping.FormatIndex.HasValue) - { - if (arguments[mapping.FormatIndex.Value] is ConstantExpression co) - { - format = (string)co.Value!; - } - else - { - throw new InvalidOperationException("The 'format' argument must be a constant expression"); - } - } + var options = (ConvertOptions)constantExpression.Value; - if (mapping.SubTypeIndex.HasValue) + if (options.OnErrorWasSet) { - if (arguments[mapping.SubTypeIndex.Value] is ConstantExpression co) - { - subType = (BsonBinarySubType)co.Value!; - } - else - { - throw new InvalidOperationException("The 'subType' argument must be a constant expression"); - } + onErrorAst = options.GetOnError(); } - if (mapping.OptionsIndex.HasValue) + if (options.OnNullWasSet) { - if (arguments[mapping.OptionsIndex.Value] is ConstantExpression co) - { - options = (ConvertOptions)co.Value!; - - if (options == null) - { - throw new InvalidOperationException("The 'options' argument cannot be null"); - } - - if (ToNumericalMethodsWithOptions.Contains(method) && !options.OnNullWasSet) - { - throw new InvalidOperationException("When converting to a non-nullable type, you need to set 'onNull'"); - } + onNullAst = options.GetOnNull(); + } - if (options.OnErrorWasSet) - { - onErrorAst = options.GetOnError(); - } + var toType = method.GetGenericArguments()[1]; + var toBsonType = GetBsonType(toType).Render(); + var serializer = BsonSerializer.LookupSerializer(toType); - if (options.OnNullWasSet) - { - onNullAst = options.GetOnNull(); - } - } - else - { - throw new InvalidOperationException("The 'options' argument must be a constant expression"); - } - } + var ast = AstExpression.Convert(fieldAst, toBsonType, subType: options.SubType, byteOrder: options.ByteOrder, format: options.Format, onError: onErrorAst, onNull: onNullAst); + return new TranslatedExpression(expression, ast, serializer); + } - var ast = AstExpression.Convert(fieldAst, mapping.Type.Render(), subType: subType, byteOrder: byteOrder, format: format, onError: onErrorAst, onNull: onNullAst); - return new TranslatedExpression(expression, ast, mapping.Serializer); + public static BsonType GetBsonType(Type type) + { + return Type.GetTypeCode(Nullable.GetUnderlyingType(type) ?? type) switch + { + TypeCode.Boolean => BsonType.Boolean, + TypeCode.Byte => BsonType.Int32, + TypeCode.SByte => BsonType.Int32, + TypeCode.Int16 => BsonType.Int32, + TypeCode.UInt16 => BsonType.Int32, + TypeCode.Int32 => BsonType.Int32, + TypeCode.UInt32 => BsonType.Int64, + TypeCode.Int64 => BsonType.Int64, + TypeCode.UInt64 => BsonType.Decimal128, + TypeCode.Single => BsonType.Double, + TypeCode.Double => BsonType.Double, + TypeCode.Decimal => BsonType.Decimal128, + TypeCode.String => BsonType.String, + TypeCode.Char => BsonType.String, + TypeCode.DateTime => BsonType.DateTime, + TypeCode.Object when type == typeof(byte[]) => BsonType.Binary, + TypeCode.Object when type == typeof(BsonBinaryData) => BsonType.Binary, + _ when type == typeof(Guid) => BsonType.Binary, + _ when type == typeof(ObjectId) => BsonType.ObjectId, + _ => BsonType.Document + }; } } } diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ToStringMethodToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ToStringMethodToAggregationExpressionTranslator.cs index bf54f764a3e..79a62e7f055 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ToStringMethodToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ToStringMethodToAggregationExpressionTranslator.cs @@ -58,11 +58,6 @@ public static TranslatedExpression Translate(TranslationContext context, MethodC return TranslateDateTimeToStringMethod(context, expression, method, arguments); } - if (ConvertMethodToAggregationExpressionTranslator.IsConvertToStringMethod(method)) - { - return ConvertMethodToAggregationExpressionTranslator.Translate(context, expression); - } - throw new ExpressionNotSupportedException(expression); } diff --git a/src/MongoDB.Driver/Mql.cs b/src/MongoDB.Driver/Mql.cs index 65cdcb191c9..5b502f2c970 100644 --- a/src/MongoDB.Driver/Mql.cs +++ b/src/MongoDB.Driver/Mql.cs @@ -49,358 +49,17 @@ public static TValue Constant(TValue value, BsonType representaion) throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } - /// - /// Converts a string to a BsonBinaryData using the $convert aggregation operator. - /// - /// The return value could be BsonNull. - /// The value. - /// The BsonBinaryData subtype of the result value. - /// The format string. - /// The converted value. - public static BsonValue ToBsonBinaryData(string value, BsonBinarySubType subtype, string format) - { - throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); - } - - /// - /// Converts a string to BsonBinaryData using the $convert aggregation operator. - /// - /// The return value could be BsonNull. - /// The value. - /// The BsonBinaryData subtype of the result value. - /// The format string. - /// The convert options. - /// The converted value. - public static BsonValue ToBsonBinaryData(string value, BsonBinarySubType subtype, string format, ConvertOptions options) - { - throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); - } - - /// - /// Converts an int to BsonBinaryData using the $convert aggregation operator. - /// - /// The return value could be BsonNull. - /// The value. - /// The BsonBinaryData subtype of the result value. - /// The byte order of BsonBinaryData. - /// The converted value. - public static BsonValue ToBsonBinaryData(int value, BsonBinarySubType subtype, ByteOrder byteOrder) - { - throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); - } - - /// - /// Converts an int to a BsonBinaryData using the $convert aggregation operator. - /// - /// The value. - /// The return value could be BsonNull. - /// The BsonBinaryData subtype of the result value. - /// The byte ordering of BsonBinaryData. - /// The convert options. - /// The converted value. - /// - public static BsonValue ToBsonBinaryData(int value, BsonBinarySubType subtype, ByteOrder byteOrder, ConvertOptions options) - { - throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); - } - - /// - /// Converts an int to BsonBinaryData using the $convert aggregation operator. - /// - /// The return value could be BsonNull. - /// The value. - /// The BsonBinaryData subtype of the result value. - /// The byte order of BsonBinaryData. - /// The converted value. - public static BsonValue ToBsonBinaryData(int? value, BsonBinarySubType subtype, ByteOrder byteOrder) - { - throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); - } - - /// - /// Converts an int? to a BsonBinaryData using the $convert aggregation operator. - /// - /// The value. - /// The return value could be BsonNull. - /// The BsonBinaryData subtype of the result value. - /// The byte ordering of BsonBinaryData. - /// The convert options. - /// The converted value. - /// - public static BsonValue ToBsonBinaryData(int? value, BsonBinarySubType subtype, ByteOrder byteOrder, ConvertOptions options) - { - throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); - } - - /// - /// Converts a long to a BsonBinaryData using the $convert aggregation operator. - /// - /// The value. - /// The return value could be BsonNull. - /// The BsonBinaryData subtype of the result value. - /// The byte ordering of BsonBinaryData. - /// The converted value. - public static BsonValue ToBsonBinaryData(long value, BsonBinarySubType subtype, ByteOrder byteOrder) - { - throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); - } - - /// - /// Converts a long to a BsonBinaryData using the $convert aggregation operator. - /// - /// The return value could be BsonNull. - /// The value. - /// The BsonBinaryData subtype of the result value. - /// The byte ordering of BsonBinaryData. - /// The convert options. - /// The converted value. - /// - public static BsonValue ToBsonBinaryData(long value, BsonBinarySubType subtype, ByteOrder byteOrder, ConvertOptions options) - { - throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); - } - - /// - /// Converts a long? to a BsonBinaryData using the $convert aggregation operator. - /// - /// The value. - /// The return value could be BsonNull. - /// The BsonBinaryData subtype of the result value. - /// The byte ordering of BsonBinaryData. - /// The converted value. - public static BsonValue ToBsonBinaryData(long? value, BsonBinarySubType subtype, ByteOrder byteOrder) - { - throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); - } - - /// - /// Converts a long? to a BsonBinaryData using the $convert aggregation operator. - /// - /// The return value could be BsonNull. - /// The value. - /// The BsonBinaryData subtype of the result value. - /// The byte ordering of BsonBinaryData. - /// The convert options. - /// The converted value. - /// - public static BsonValue ToBsonBinaryData(long? value, BsonBinarySubType subtype, ByteOrder byteOrder, ConvertOptions options) - { - throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); - } - - /// - /// Converts a double to a BsonBinaryData using the $convert aggregation operator. - /// - /// The return value could be BsonNull. - /// The value. - /// The BsonBinaryData subtype of the result value. - /// The byte ordering of BsonBinaryData. - /// The converted value. - public static BsonValue ToBsonBinaryData(double value, BsonBinarySubType subtype, ByteOrder byteOrder) - { - throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); - } - - /// - /// Converts a double to a BsonBinaryData using the $convert aggregation operator. - /// - /// The return value could be BsonNull. - /// The field. - /// The BsonBinaryData subtype of the result value. - /// The byte ordering of BsonBinaryData. - /// The convert options. - /// The converted value. - /// - public static BsonValue ToBsonBinaryData(double value, BsonBinarySubType subtype, ByteOrder byteOrder, ConvertOptions options) - { - throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); - } - - /// - /// Converts a double? to a BsonBinaryData using the $convert aggregation operator. - /// - /// The return value could be BsonNull. - /// The value. - /// The BsonBinaryData subtype of the result value. - /// The byte ordering of BsonBinaryData. - /// The converted value. - public static BsonValue ToBsonBinaryData(double? value, BsonBinarySubType subtype, ByteOrder byteOrder) - { - throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); - } - - /// - /// Converts a double? to a BsonBinaryData using the $convert aggregation operator. - /// - /// The return value could be BsonNull. - /// The field. - /// The BsonBinaryData subtype of the result value. - /// The byte ordering of BsonBinaryData. - /// The convert options. - /// The converted value. - /// - public static BsonValue ToBsonBinaryData(double? value, BsonBinarySubType subtype, ByteOrder byteOrder, ConvertOptions options) - { - throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); - } - - /// - /// Converts a BsonBinaryData to double using the $convert aggregation operator. - /// - /// The value. - /// The byte ordering of BsonBinaryData. - /// The converted value. - public static double ToDouble(BsonBinaryData value, ByteOrder byteOrder) - { - throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); - } - - /// - /// Converts a BsonBinaryData to double using the $convert aggregation operator. - /// - /// The value. - /// The byte ordering of BsonBinaryData. - /// The convert options. - /// The converted value. - public static double ToDouble(BsonBinaryData value, ByteOrder byteOrder, ConvertOptions options) - { - throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); - } - - /// - /// Converts a BsonBinaryData to int using the $convert aggregation operator. - /// - /// The value. - /// The byte ordering of BsonBinaryData. - /// The converted value. - public static int ToInt(BsonBinaryData value, ByteOrder byteOrder) - { - throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); - } /// - /// Converts a BsonBinaryData to int using the $convert aggregation operator. + /// Converts a value from one type to another using the specified options. /// - /// The value. - /// The byte ordering of BsonBinaryData. - /// The convert options. + /// The type of the input value. + /// The type of the output value. + /// The value to convert. + /// The conversion options. /// The converted value. - public static int ToInt(BsonBinaryData value, ByteOrder byteOrder, ConvertOptions options) - { - throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); - } - - /// - /// Converts a BsonBinaryData to long using the $convert aggregation operator. - /// - /// The value. - /// The byte ordering of BsonBinaryData. - /// The converted value. - public static long ToLong(BsonBinaryData value, ByteOrder byteOrder) - { - throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); - } - - /// - /// Converts a BsonBinaryData to long using the $convert aggregation operator. - /// - /// The value. - /// The byte ordering of BsonBinaryData. - /// The convert options. - /// The converted value. - public static long ToLong(BsonBinaryData value, ByteOrder byteOrder, ConvertOptions options) - { - throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); - } - - /// - /// Converts a BsonBinaryData to a string using the $convert aggregation operator. - /// - /// The value. - /// The format string. - /// The converted value. - public static string ToString(BsonBinaryData value, string format) - { - throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); - } - - /// - /// Converts a BsonBinaryData to a string using the $convert aggregation operator. - /// - /// The value. - /// The format string. - /// The convert options. - /// The converted value. - public static string ToString(BsonBinaryData value, string format, ConvertOptions options) - { - throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); - } - - /// - /// Converts a BsonBinaryData to int? using the $convert aggregation operator. - /// - /// The value. - /// The byte ordering of BsonBinaryData. - /// The converted value. - public static int? ToNullableInt(BsonBinaryData value, ByteOrder byteOrder) - { - throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); - } - - /// - /// Converts a BsonBinaryData to int? using the $convert aggregation operator. - /// - /// The value. - /// The byte ordering of BsonBinaryData. - /// The convert options. - /// The converted value. - public static int? ToNullableInt(BsonBinaryData value, ByteOrder byteOrder, ConvertOptions options) - { - throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); - } - - /// - /// Converts a BsonBinaryData to long? using the $convert aggregation operator. - /// - /// The value. - /// The byte ordering of BsonBinaryData. - /// The converted value. - public static long? ToNullableLong(BsonBinaryData value, ByteOrder byteOrder) - { - throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); - } - - /// - /// Converts a BsonBinaryData to long? using the $convert aggregation operator. - /// - /// The value. - /// The byte ordering of BsonBinaryData. - /// The convert options. - /// The converted value. - public static long? ToNullableLong(BsonBinaryData value, ByteOrder byteOrder, ConvertOptions options) - { - throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); - } - - /// - /// Converts a BsonBinaryData to double? using the $convert aggregation operator. - /// - /// The value. - /// The byte ordering of BsonBinaryData. - /// The converted value. - public static double? ToNullableDouble(BsonBinaryData value, ByteOrder byteOrder) - { - throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); - } - - /// - /// Converts a BsonBinaryData to string using the $convert aggregation operator. - /// - /// The value. - /// The byte ordering of BsonBinaryData. - /// The convert options. - /// The converted value. - public static double? ToNullableDouble(BsonBinaryData value, ByteOrder byteOrder, ConvertOptions options) + /// Thrown when the method is not supported. + public static TTo Convert(TFrom value, ConvertOptions options) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } @@ -530,6 +189,37 @@ public enum ByteOrder /// public abstract class ConvertOptions { + private ByteOrder? _byteOrder; + private string _format; + private BsonBinarySubType? _subType; + + /// + /// The byteOrder parameter. + /// + public ByteOrder? ByteOrder + { + get => _byteOrder; + set => _byteOrder = value; + } + + /// + /// The format parameter. + /// + public string Format + { + get => _format; + set => _format = value; + } + + /// + /// The subType parameter. + /// + public BsonBinarySubType? SubType + { + get => _subType; + set => _subType = value; + } + internal abstract bool OnErrorWasSet { get; } internal abstract bool OnNullWasSet { get; } diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs index a2246ed92ca..5d9d4e78371 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs @@ -33,112 +33,6 @@ public ConvertMethodToAggregationExpressionTranslatorTests(ClassFixture fixture) { } - // To BinData - - [Fact] - public void MongoDBFunctions_ToBsonBinaryDataFromString_when_options_is_null_should_throw() - { - RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); - - var collection = Fixture.Collection; - var queryable = collection.AsQueryable() - .Where(x => x.Id == 1) - .Select(x => Mql.ToBsonBinaryData(x.StringProperty, BsonBinarySubType.Binary, "format", null)); - - var exception = Record.Exception(() => Translate(collection, queryable)); - Assert.IsType(exception); - Assert.Equal("The 'options' argument cannot be null", exception.Message); - } - - [Fact] - public void MongoDBFunctions_ToBsonBinaryDataFromInt_when_options_is_null_should_throw() - { - RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); - - var collection = Fixture.Collection; - var queryable = collection.AsQueryable() - .Where(x => x.Id == 1) - .Select(x => Mql.ToBsonBinaryData(x.IntProperty, BsonBinarySubType.Binary, ByteOrder.BigEndian, null)); - - var exception = Record.Exception(() => Translate(collection, queryable)); - Assert.IsType(exception); - Assert.Equal("The 'options' argument cannot be null", exception.Message); - } - - [Fact] - public void MongoDBFunctions_ToBsonBinaryDataFromNullableInt_when_options_is_null_should_throw() - { - RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); - - var collection = Fixture.Collection; - var queryable = collection.AsQueryable() - .Where(x => x.Id == 1) - .Select(x => Mql.ToBsonBinaryData(x.NullableIntProperty, BsonBinarySubType.Binary, ByteOrder.BigEndian, null)); - - var exception = Record.Exception(() => Translate(collection, queryable)); - Assert.IsType(exception); - Assert.Equal("The 'options' argument cannot be null", exception.Message); - } - - [Fact] - public void MongoDBFunctions_ToBsonBinaryDataFromDouble_when_options_is_null_should_throw() - { - RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); - - var collection = Fixture.Collection; - var queryable = collection.AsQueryable() - .Where(x => x.Id == 1) - .Select(x => Mql.ToBsonBinaryData(x.DoubleProperty, BsonBinarySubType.Binary, ByteOrder.BigEndian, null)); - - var exception = Record.Exception(() => Translate(collection, queryable)); - Assert.IsType(exception); - Assert.Equal("The 'options' argument cannot be null", exception.Message); - } - - [Fact] - public void MongoDBFunctions_ToBsonBinaryDataFromNullableDouble_when_options_is_null_should_throw() - { - RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); - - var collection = Fixture.Collection; - var queryable = collection.AsQueryable() - .Where(x => x.Id == 1) - .Select(x => Mql.ToBsonBinaryData(x.NullableDoubleProperty, BsonBinarySubType.Binary, ByteOrder.BigEndian, null)); - - var exception = Record.Exception(() => Translate(collection, queryable)); - Assert.IsType(exception); - Assert.Equal("The 'options' argument cannot be null", exception.Message); - } - - [Fact] - public void MongoDBFunctions_ToBsonBinaryDataFromLong_when_options_is_null_should_throw() - { - RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); - - var collection = Fixture.Collection; - var queryable = collection.AsQueryable() - .Where(x => x.Id == 1) - .Select(x => Mql.ToBsonBinaryData(x.LongProperty, BsonBinarySubType.Binary, ByteOrder.BigEndian, null)); - - var exception = Record.Exception(() => Translate(collection, queryable)); - Assert.IsType(exception); - Assert.Equal("The 'options' argument cannot be null", exception.Message); - } - - [Fact] - public void MongoDBFunctions_ToBsonBinaryDataFromNullableLong_when_options_is_null_should_throw() - { - RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); - - var collection = Fixture.Collection; - var queryable = collection.AsQueryable() - .Where(x => x.Id == 1) - .Select(x => Mql.ToBsonBinaryData(x.NullableLongProperty, BsonBinarySubType.Binary, ByteOrder.BigEndian, null)); - - var exception = Record.Exception(() => Translate(collection, queryable)); - Assert.IsType(exception); - Assert.Equal("The 'options' argument cannot be null", exception.Message); - } [Theory] [InlineData(3, ByteOrder.LittleEndian,"AAAAAAAA4L8=", null)] @@ -151,7 +45,7 @@ public void MongoDBFunctions_ToBsonBinaryDataFromDouble_should_work(int id, Byte var collection = Fixture.Collection; var queryable = collection.AsQueryable() .Where(x => x.Id == id) - .Select(x => Mql.ToBsonBinaryData(x.DoubleProperty, BsonBinarySubType.Binary, byteOrder)); + .Select(x => Mql.Convert(x.DoubleProperty, new ConvertOptions { ByteOrder = byteOrder, SubType = BsonBinarySubType.Binary })); var expectedStages = new[] @@ -160,991 +54,10 @@ public void MongoDBFunctions_ToBsonBinaryDataFromDouble_should_work(int id, Byte $"{{ $project: {{ _v : {{ $convert : {{ input : '$DoubleProperty', to : {{ type: 'binData', subtype: 0 }}, {ByteOrderToString(byteOrder)} }} }}, _id : 0 }} }}", }; - BsonValue expectedResult = expectedBase64 is null? BsonNull.Value : new BsonBinaryData(Convert.FromBase64String(expectedBase64)); - AssertOutcome(collection, queryable, expectedStages, expectedResult, expectedException); - } - - [Theory] - [InlineData(1, ByteOrder.LittleEndian,null, null)] - [InlineData(3, ByteOrder.LittleEndian,"AAAAAAAA4L8=", null)] - [InlineData(5, ByteOrder.BigEndian, "wAQAAAAAAAA=", null )] - [InlineData(10, ByteOrder.BigEndian, null, "MongoCommandException")] - public void MongoDBFunctions_ToBsonBinaryDataFromNullableDouble_should_work(int id, ByteOrder byteOrder, string expectedBase64, string expectedException) - { - RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); - - var collection = Fixture.Collection; - var queryable = collection.AsQueryable() - .Where(x => x.Id == id) - .Select(x => Mql.ToBsonBinaryData(x.NullableDoubleProperty, BsonBinarySubType.Binary, byteOrder)); - - var expectedStages = - new[] - { - $"{{ $match : {{ _id : {id} }} }}", - $"{{ $project: {{ _v : {{ $convert : {{ input : '$NullableDoubleProperty', to : {{ type: 'binData', subtype: 0 }}, {ByteOrderToString(byteOrder)} }} }}, _id : 0 }} }}", - }; - - BsonValue expectedResult = expectedBase64 is null? BsonNull.Value : new BsonBinaryData(Convert.FromBase64String(expectedBase64)); - AssertOutcome(collection, queryable, expectedStages, expectedResult, expectedException); - } - - [Theory] - [InlineData(0, ByteOrder.LittleEndian, "AAAAAAAABMA=", true, "Ag==", true, "AAAAAAAABMA=")] - [InlineData(10, ByteOrder.LittleEndian, "Ag==", true, "Ag==", true, "AAAAAAAABMA=")] - [InlineData(0, ByteOrder.BigEndian, "AAAAAAAABMA=", true, "Ag==", true, "AAAAAAAABMA=")] - [InlineData(10, ByteOrder.BigEndian, "Ag==", true, "Ag==", true, "AAAAAAAABMA=")] - [InlineData(0, ByteOrder.BigEndian, null, true, "Ag==", true, null)] - [InlineData(10, ByteOrder.BigEndian, null, true, null, true, "AAAAAAAABMA=")] - public void MongoDBFunctions_ToBsonBinaryDataFromDoubleWithOptions_should_work(int id, ByteOrder byteOrder, string expectedBase64, bool setOnError, string onErrorBase64, bool setOnNull, string onNullBase64) - { - RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); - - var options = new ConvertOptions(); - if (setOnError) options.OnError = onErrorBase64 == null ? BsonNull.Value : new BsonBinaryData(Convert.FromBase64String(onErrorBase64)); - if (setOnNull) options.OnNull = onNullBase64 == null ? BsonNull.Value : new BsonBinaryData(Convert.FromBase64String(onNullBase64)); - - var collection = Fixture.Collection; - var queryable = collection.AsQueryable() - .Where(x => x.Id == id) - .Select(x => Mql.ToBsonBinaryData(x.DoubleProperty, BsonBinarySubType.Binary, byteOrder, options)); - - var onErrorStr = setOnError ? $"onError: {FormatBase64(onErrorBase64)}," : ""; - var onNullStr = setOnNull ? $"onNull: {FormatBase64(onNullBase64)}," : ""; - - var expectedStages = - new[] - { - $"{{ $match : {{ _id : {id} }} }}", - $"{{ $project: {{ _v : {{ $convert : {{ input : '$DoubleProperty', to : {{ type: 'binData', subtype: 0 }}, {onErrorStr} {onNullStr} {ByteOrderToString(byteOrder)}}} }}, _id : 0 }} }}", - }; - - BsonValue expectedResult = expectedBase64 is null? BsonNull.Value : new BsonBinaryData(Convert.FromBase64String(expectedBase64)); - AssertOutcome(collection, queryable, expectedStages, expectedResult); - } - - [Theory] - [InlineData(0, ByteOrder.LittleEndian, "AAAAAAAABMA=", true, "Ag==", true, "AAAAAAAABMA=")] - [InlineData(10, ByteOrder.LittleEndian, "Ag==", true, "Ag==", true, "AAAAAAAABMA=")] - [InlineData(0, ByteOrder.BigEndian, "AAAAAAAABMA=", true, "Ag==", true, "AAAAAAAABMA=")] - [InlineData(10, ByteOrder.BigEndian, "Ag==", true, "Ag==", true, "AAAAAAAABMA=")] - [InlineData(0, ByteOrder.BigEndian, null, true, "Ag==", true, null)] - [InlineData(10, ByteOrder.BigEndian, null, true, null, true, "AAAAAAAABMA=")] - [InlineData(0, ByteOrder.BigEndian, null, false, "Ag==", true, null)] - [InlineData(10, ByteOrder.BigEndian, null, true, null, false, "AAAAAAAABMA=")] - public void MongoDBFunctions_ToBsonBinaryDataFromNullableDoubleWithOptions_should_work(int id, ByteOrder byteOrder, string expectedBase64, bool setOnError, string onErrorBase64, bool setOnNull, string onNullBase64) - { - RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); - - var options = new ConvertOptions(); - if (setOnError) options.OnError = onErrorBase64 == null ? BsonNull.Value : new BsonBinaryData(Convert.FromBase64String(onErrorBase64)); - if (setOnNull) options.OnNull = onNullBase64 == null ? BsonNull.Value : new BsonBinaryData(Convert.FromBase64String(onNullBase64)); - - var collection = Fixture.Collection; - var queryable = collection.AsQueryable() - .Where(x => x.Id == id) - .Select(x => Mql.ToBsonBinaryData(x.NullableDoubleProperty, BsonBinarySubType.Binary, byteOrder, options)); - - var onErrorStr = setOnError ? $"onError: {FormatBase64(onErrorBase64)}," : ""; - var onNullStr = setOnNull ? $"onNull: {FormatBase64(onNullBase64)}," : ""; - - var expectedStages = - new[] - { - $"{{ $match : {{ _id : {id} }} }}", - $"{{ $project: {{ _v : {{ $convert : {{ input : '$NullableDoubleProperty', to : {{ type: 'binData', subtype: 0 }}, {onErrorStr} {onNullStr} {ByteOrderToString(byteOrder)}}} }}, _id : 0 }} }}", - }; - - BsonValue expectedResult = expectedBase64 is null? BsonNull.Value : new BsonBinaryData(Convert.FromBase64String(expectedBase64)); - AssertOutcome(collection, queryable, expectedStages, expectedResult); - } - - [Theory] - [InlineData(4, ByteOrder.LittleEndian,"ogIAAA==", null)] - [InlineData(6, ByteOrder.BigEndian, "AAAAKg==", null )] - [InlineData(10, ByteOrder.BigEndian, null, "MongoCommandException")] - public void MongoDBFunctions_ToBsonBinaryDataFromInt_should_work(int id, ByteOrder byteOrder, string expectedBase64, string expectedException) - { - RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); - - var collection = Fixture.Collection; - var queryable = collection.AsQueryable() - .Where(x => x.Id == id) - .Select(x => Mql.ToBsonBinaryData(x.IntProperty, BsonBinarySubType.Binary, byteOrder)); - - var expectedStages = - new[] - { - $"{{ $match : {{ _id : {id} }} }}", - $"{{ $project: {{ _v : {{ $convert : {{ input : '$IntProperty', to : {{ type: 'binData', subtype: 0 }}, {ByteOrderToString(byteOrder)} }} }}, _id : 0 }} }}", - }; - - BsonValue expectedResult = expectedBase64 is null? BsonNull.Value : new BsonBinaryData(Convert.FromBase64String(expectedBase64)); - AssertOutcome(collection, queryable, expectedStages, expectedResult, expectedException); - } - - [Theory] - [InlineData(1, ByteOrder.LittleEndian,null, null)] - [InlineData(4, ByteOrder.LittleEndian,"ogIAAA==", null)] - [InlineData(6, ByteOrder.BigEndian, "AAAAKg==", null )] - [InlineData(10, ByteOrder.BigEndian, null, "MongoCommandException")] - public void MongoDBFunctions_ToBsonBinaryDataFromNullableInt_should_work(int id, ByteOrder byteOrder, string expectedBase64, string expectedException) - { - RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); - - var collection = Fixture.Collection; - var queryable = collection.AsQueryable() - .Where(x => x.Id == id) - .Select(x => Mql.ToBsonBinaryData(x.NullableIntProperty, BsonBinarySubType.Binary, byteOrder)); - - var expectedStages = - new[] - { - $"{{ $match : {{ _id : {id} }} }}", - $"{{ $project: {{ _v : {{ $convert : {{ input : '$NullableIntProperty', to : {{ type: 'binData', subtype: 0 }}, {ByteOrderToString(byteOrder)} }} }}, _id : 0 }} }}", - }; - - BsonValue expectedResult = expectedBase64 is null? BsonNull.Value : new BsonBinaryData(Convert.FromBase64String(expectedBase64)); - AssertOutcome(collection, queryable, expectedStages, expectedResult, expectedException); - } - - [Theory] - [InlineData(0, ByteOrder.LittleEndian, "AAAAAAAABMA=", true, "Ag==", true, "AAAAAAAABMA=")] - [InlineData(10, ByteOrder.LittleEndian, "Ag==", true, "Ag==", true, "AAAAAAAABMA=")] - [InlineData(0, ByteOrder.BigEndian, "AAAAAAAABMA=", true, "Ag==", true, "AAAAAAAABMA=")] - [InlineData(10, ByteOrder.BigEndian, "Ag==", true, "Ag==", true, "AAAAAAAABMA=")] - [InlineData(0, ByteOrder.BigEndian, null, true, "Ag==", true, null)] - [InlineData(10, ByteOrder.BigEndian, null, true, null, true, "AAAAAAAABMA=")] - public void MongoDBFunctions_ToBsonBinaryDataFromIntWithOptions_should_work(int id, ByteOrder byteOrder, string expectedBase64, bool setOnError, string onErrorBase64, bool setOnNull, string onNullBase64) - { - RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); - - var options = new ConvertOptions(); - if (setOnError) options.OnError = onErrorBase64 == null ? BsonNull.Value : new BsonBinaryData(Convert.FromBase64String(onErrorBase64)); - if (setOnNull) options.OnNull = onNullBase64 == null ? BsonNull.Value : new BsonBinaryData(Convert.FromBase64String(onNullBase64)); - - var collection = Fixture.Collection; - var queryable = collection.AsQueryable() - .Where(x => x.Id == id) - .Select(x => Mql.ToBsonBinaryData(x.IntProperty, BsonBinarySubType.Binary, byteOrder, options)); - - var onErrorStr = setOnError ? $"onError: {FormatBase64(onErrorBase64)}," : ""; - var onNullStr = setOnNull ? $"onNull: {FormatBase64(onNullBase64)}," : ""; - - var expectedStages = - new[] - { - $"{{ $match : {{ _id : {id} }} }}", - $"{{ $project: {{ _v : {{ $convert : {{ input : '$IntProperty', to : {{ type: 'binData', subtype: 0 }}, {onErrorStr} {onNullStr} {ByteOrderToString(byteOrder)}}} }}, _id : 0 }} }}", - }; - - BsonValue expectedResult = expectedBase64 is null? BsonNull.Value : new BsonBinaryData(Convert.FromBase64String(expectedBase64)); - AssertOutcome(collection, queryable, expectedStages, expectedResult); - } - - [Theory] - [InlineData(0, ByteOrder.LittleEndian, "AAAAAAAABMA=", true, "Ag==", true, "AAAAAAAABMA=")] - [InlineData(10, ByteOrder.LittleEndian, "Ag==", true, "Ag==", true, "AAAAAAAABMA=")] - [InlineData(0, ByteOrder.BigEndian, "AAAAAAAABMA=", true, "Ag==", true, "AAAAAAAABMA=")] - [InlineData(10, ByteOrder.BigEndian, "Ag==", true, "Ag==", true, "AAAAAAAABMA=")] - [InlineData(0, ByteOrder.BigEndian, null, true, "Ag==", true, null)] - [InlineData(10, ByteOrder.BigEndian, null, true, null, true, "AAAAAAAABMA=")] - [InlineData(0, ByteOrder.BigEndian, null, false, "Ag==", true, null)] - [InlineData(10, ByteOrder.BigEndian, null, true, null, false, "AAAAAAAABMA=")] - public void MongoDBFunctions_ToBsonBinaryDataFromNullableIntWithOptions_should_work(int id, ByteOrder byteOrder, string expectedBase64, bool setOnError, string onErrorBase64, bool setOnNull, string onNullBase64) - { - RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); - - var options = new ConvertOptions(); - if (setOnError) options.OnError = onErrorBase64 == null ? BsonNull.Value : new BsonBinaryData(Convert.FromBase64String(onErrorBase64)); - if (setOnNull) options.OnNull = onNullBase64 == null ? BsonNull.Value : new BsonBinaryData(Convert.FromBase64String(onNullBase64)); - - var collection = Fixture.Collection; - var queryable = collection.AsQueryable() - .Where(x => x.Id == id) - .Select(x => Mql.ToBsonBinaryData(x.NullableIntProperty, BsonBinarySubType.Binary, byteOrder, options)); - - var onErrorStr = setOnError ? $"onError: {FormatBase64(onErrorBase64)}," : ""; - var onNullStr = setOnNull ? $"onNull: {FormatBase64(onNullBase64)}," : ""; - - var expectedStages = - new[] - { - $"{{ $match : {{ _id : {id} }} }}", - $"{{ $project: {{ _v : {{ $convert : {{ input : '$NullableIntProperty', to : {{ type: 'binData', subtype: 0 }}, {onErrorStr} {onNullStr} {ByteOrderToString(byteOrder)}}} }}, _id : 0 }} }}", - }; - - BsonValue expectedResult = expectedBase64 is null? BsonNull.Value : new BsonBinaryData(Convert.FromBase64String(expectedBase64)); - AssertOutcome(collection, queryable, expectedStages, expectedResult); - } - - [Theory] - [InlineData(4, ByteOrder.LittleEndian,"ogIAAA==", null)] - [InlineData(6, ByteOrder.BigEndian, "AAAAKg==", null )] - [InlineData(10, ByteOrder.BigEndian, null, "MongoCommandException")] - public void MongoDBFunctions_ToBsonBinaryDataFromLong_should_work(int id, ByteOrder byteOrder, string expectedBase64, string expectedException) - { - RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); - - var collection = Fixture.Collection; - var queryable = collection.AsQueryable() - .Where(x => x.Id == id) - .Select(x => Mql.ToBsonBinaryData(x.LongProperty, BsonBinarySubType.Binary, byteOrder)); - - var expectedStages = - new[] - { - $"{{ $match : {{ _id : {id} }} }}", - $"{{ $project: {{ _v : {{ $convert : {{ input : '$LongProperty', to : {{ type: 'binData', subtype: 0 }}, {ByteOrderToString(byteOrder)} }} }}, _id : 0 }} }}", - }; - - BsonValue expectedResult = null; - if (expectedBase64 is not null) - { - //$convert to bindata returns always 8 bytes when from long - var expectedBytes = new byte[8]; - Array.Copy(Convert.FromBase64String(expectedBase64), 0, expectedBytes, byteOrder is ByteOrder.LittleEndian ? 0 : 4, 4); - expectedResult = new BsonBinaryData(expectedBytes); - } - else - { - expectedResult = BsonNull.Value; - } - - AssertOutcome(collection, queryable, expectedStages, expectedResult, expectedException); - } - - [Theory] - [InlineData(1, ByteOrder.LittleEndian,null, null)] - [InlineData(4, ByteOrder.LittleEndian,"ogIAAA==", null)] - [InlineData(6, ByteOrder.BigEndian, "AAAAKg==", null )] - [InlineData(10, ByteOrder.BigEndian, null, "MongoCommandException")] - public void MongoDBFunctions_ToBsonBinaryDataFromNullableLong_should_work(int id, ByteOrder byteOrder, string expectedBase64, string expectedException) - { - RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); - - var collection = Fixture.Collection; - var queryable = collection.AsQueryable() - .Where(x => x.Id == id) - .Select(x => Mql.ToBsonBinaryData(x.NullableLongProperty, BsonBinarySubType.Binary, byteOrder)); - - var expectedStages = - new[] - { - $"{{ $match : {{ _id : {id} }} }}", - $"{{ $project: {{ _v : {{ $convert : {{ input : '$NullableLongProperty', to : {{ type: 'binData', subtype: 0 }}, {ByteOrderToString(byteOrder)} }} }}, _id : 0 }} }}", - }; - - BsonValue expectedResult = null; - if (expectedBase64 is not null) - { - //$convert to bindata returns always 8 bytes when from long - var expectedBytes = new byte[8]; - Array.Copy(Convert.FromBase64String(expectedBase64), 0, expectedBytes, byteOrder is ByteOrder.LittleEndian ? 0 : 4, 4); - expectedResult = new BsonBinaryData(expectedBytes); - } - else - { - expectedResult = BsonNull.Value; - } - + var expectedResult = new BsonBinaryData(Convert.FromBase64String(expectedBase64)); AssertOutcome(collection, queryable, expectedStages, expectedResult, expectedException); } - [Theory] - [InlineData(0, ByteOrder.LittleEndian, "AAAAAAAABMA=", true, "AAAAAAAA4L8=", true, "AAAAAAAABMA=")] - [InlineData(10, ByteOrder.LittleEndian, "AAAAAAAA4L8=", true, "AAAAAAAA4L8=", true, "AAAAAAAABMA=")] - [InlineData(0, ByteOrder.BigEndian, "AAAAAAAABMA=", true, "AAAAAAAA4L8=", true, "AAAAAAAABMA=")] - [InlineData(10, ByteOrder.BigEndian, "AAAAAAAA4L8=", true, "AAAAAAAA4L8=", true, "AAAAAAAABMA=")] - [InlineData(0, ByteOrder.BigEndian, null, true, "AAAAAAAA4L8=", true, null)] - [InlineData(10, ByteOrder.BigEndian, null, true, null, true, "AAAAAAAABMA=")] - public void MongoDBFunctions_ToBsonBinaryDataFromLongWithOptions_should_work(int id, ByteOrder byteOrder, string expectedBase64, bool setOnError, string onErrorBase64, bool setOnNull, string onNullBase64) - { - RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); - - var options = new ConvertOptions(); - if (setOnError) options.OnError = onErrorBase64 == null ? BsonNull.Value : new BsonBinaryData(Convert.FromBase64String(onErrorBase64)); - if (setOnNull) options.OnNull = onNullBase64 == null ? BsonNull.Value : new BsonBinaryData(Convert.FromBase64String(onNullBase64)); - - var collection = Fixture.Collection; - var queryable = collection.AsQueryable() - .Where(x => x.Id == id) - .Select(x => Mql.ToBsonBinaryData(x.LongProperty, BsonBinarySubType.Binary, byteOrder, options)); - - var onErrorStr = setOnError ? $"onError: {FormatBase64(onErrorBase64)}," : ""; - var onNullStr = setOnNull ? $"onNull: {FormatBase64(onNullBase64)}," : ""; - - var expectedStages = - new[] - { - $"{{ $match : {{ _id : {id} }} }}", - $"{{ $project: {{ _v : {{ $convert : {{ input : '$LongProperty', to : {{ type: 'binData', subtype: 0 }}, {onErrorStr} {onNullStr} {ByteOrderToString(byteOrder)}}} }}, _id : 0 }} }}", - }; - - BsonValue expectedResult = expectedBase64 is null? BsonNull.Value : new BsonBinaryData(Convert.FromBase64String(expectedBase64)); - AssertOutcome(collection, queryable, expectedStages, expectedResult); - } - - [Theory] - [InlineData(0, ByteOrder.LittleEndian, "AAAAAAAABMA=", true, "AAAAAAAA4L8=", true, "AAAAAAAABMA=")] - [InlineData(10, ByteOrder.LittleEndian, "AAAAAAAA4L8=", true, "AAAAAAAA4L8=", true, "AAAAAAAABMA=")] - [InlineData(0, ByteOrder.BigEndian, "AAAAAAAABMA=", true, "AAAAAAAA4L8=", true, "AAAAAAAABMA=")] - [InlineData(10, ByteOrder.BigEndian, "AAAAAAAA4L8=", true, "AAAAAAAA4L8=", true, "AAAAAAAABMA=")] - [InlineData(0, ByteOrder.BigEndian, null, true, "AAAAAAAA4L8=", true, null)] - [InlineData(10, ByteOrder.BigEndian, null, true, null, true, "AAAAAAAABMA=")] - [InlineData(0, ByteOrder.BigEndian, null, false, "AAAAAAAA4L8=", true, null)] - [InlineData(10, ByteOrder.BigEndian, null, true, null, false, "AAAAAAAABMA=")] - public void MongoDBFunctions_ToBsonBinaryDataFromNullableLongWithOptions_should_work(int id, ByteOrder byteOrder, string expectedBase64, bool setOnError, string onErrorBase64, bool setOnNull, string onNullBase64) - { - RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); - - var options = new ConvertOptions(); - if (setOnError) options.OnError = onErrorBase64 == null ? BsonNull.Value : new BsonBinaryData(Convert.FromBase64String(onErrorBase64)); - if (setOnNull) options.OnNull = onNullBase64 == null ? BsonNull.Value : new BsonBinaryData(Convert.FromBase64String(onNullBase64)); - - var collection = Fixture.Collection; - var queryable = collection.AsQueryable() - .Where(x => x.Id == id) - .Select(x => Mql.ToBsonBinaryData(x.NullableLongProperty, BsonBinarySubType.Binary, byteOrder, options)); - - var onErrorStr = setOnError ? $"onError: {FormatBase64(onErrorBase64)}," : ""; - var onNullStr = setOnNull ? $"onNull: {FormatBase64(onNullBase64)}," : ""; - - var expectedStages = - new[] - { - $"{{ $match : {{ _id : {id} }} }}", - $"{{ $project: {{ _v : {{ $convert : {{ input : '$NullableLongProperty', to : {{ type: 'binData', subtype: 0 }}, {onErrorStr} {onNullStr} {ByteOrderToString(byteOrder)}}} }}, _id : 0 }} }}", - }; - - BsonValue expectedResult = expectedBase64 is null? BsonNull.Value : new BsonBinaryData(Convert.FromBase64String(expectedBase64)); - AssertOutcome(collection, queryable, expectedStages, expectedResult); - } - - - [Theory] - [InlineData(1, null, null)] - [InlineData(2, "867dee52-c331-484e-92d1-c56479b8e67e", null)] - [InlineData(10, null, "MongoCommandException")] - public void MongoDBFunctions_ToBsonBinaryDataFromString_should_work(int id, string expectedGuidString, string expectedException) - { - RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromString); - - var collection = Fixture.Collection; - var queryable = collection.AsQueryable() - .Where(x => x.Id == id) - .Select(x => Mql.ToBsonBinaryData(x.StringProperty, BsonBinarySubType.UuidStandard, "uuid")); - - var expectedStages = - new[] - { - $"{{ $match : {{ _id : {id} }} }}", - $"{{ $project: {{ _v : {{ $convert : {{ input : '$StringProperty', to : {{ type: 'binData', subtype: 4 }}, format: 'uuid' }} }}, _id : 0 }} }}", - }; - - BsonValue expectedResult = expectedGuidString is null? BsonNull.Value : new BsonBinaryData(Guid.Parse(expectedGuidString), GuidRepresentation.Standard); - AssertOutcome(collection, queryable, expectedStages, expectedResult, expectedException); - } - - [Theory] - [InlineData(0, "AAAAAAAABMA=", true, "Ag==", true, "AAAAAAAABMA=")] - [InlineData(10, "Ag==", true, "Ag==", true, "AAAAAAAABMA=")] - [InlineData(0, "AAAAAAAABMA=", false, "Ag==", true, "AAAAAAAABMA=")] - [InlineData(10, "Ag==", true, "Ag==", false, "AAAAAAAABMA=")] - public void MongoDBFunctions_ToBsonBinaryDataFromStringWithOptions_should_work(int id, string expectedBase64, bool setOnError, string onErrorBase64, bool setOnNull, string onNullBase64) - { - RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); - - var options = new ConvertOptions(); - if (setOnError) options.OnError = onErrorBase64 == null ? BsonNull.Value : new BsonBinaryData(Convert.FromBase64String(onErrorBase64)); - if (setOnNull) options.OnNull = onNullBase64 == null ? BsonNull.Value : new BsonBinaryData(Convert.FromBase64String(onNullBase64)); - - var collection = Fixture.Collection; - var queryable = collection.AsQueryable() - .Where(x => x.Id == id) - .Select(x => Mql.ToBsonBinaryData(x.StringProperty, BsonBinarySubType.UuidStandard, "uuid", options)); - - var onErrorStr = setOnError ? $"onError: {FormatBase64(onErrorBase64)}," : ""; - var onNullStr = setOnNull ? $"onNull: {FormatBase64(onNullBase64)}," : ""; - - var expectedStages = - new[] - { - $"{{ $match : {{ _id : {id} }} }}", - $"{{ $project: {{ _v : {{ $convert : {{ input : '$StringProperty', to : {{ type: 'binData', subtype: 4 }}, {onErrorStr} {onNullStr} format: 'uuid' }} }}, _id : 0 }} }}", - }; - - var expectedResult = expectedBase64 is null? null : new BsonBinaryData(Convert.FromBase64String(expectedBase64)); - AssertOutcome(collection, queryable, expectedStages, expectedResult); - } - - // To Double - - [Fact] - public void MongoDBFunctions_ToDoubleFromBsonBinaryData_when_onNull_is_not_set_should_throw() - { - RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); - - var options = new ConvertOptions { OnError = 22 }; - - var collection = Fixture.Collection; - var queryable = collection.AsQueryable() - .Where(x => x.Id == 1) - .Select(x => Mql.ToDouble(x.BinaryProperty, ByteOrder.LittleEndian, options)); - - var exception = Record.Exception(() => Translate(collection, queryable)); - Assert.IsType(exception); - Assert.Equal("When converting to a non-nullable type, you need to set 'onNull'", exception.Message); - } - - [Fact] - public void MongoDBFunctions_ToDoubleFromBsonBinaryData_when_options_is_null_should_throw() - { - RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); - - var collection = Fixture.Collection; - var queryable = collection.AsQueryable() - .Where(x => x.Id == 1) - .Select(x => Mql.ToDouble(x.BinaryProperty, ByteOrder.LittleEndian, null)); - - var exception = Record.Exception(() => Translate(collection, queryable)); - Assert.IsType(exception); - Assert.Equal("The 'options' argument cannot be null", exception.Message); - } - - [Fact] - public void MongoDBFunctions_ToNullableDoubleFromBsonBinaryData_when_options_is_null_should_throw() - { - RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); - - var collection = Fixture.Collection; - var queryable = collection.AsQueryable() - .Where(x => x.Id == 1) - .Select(x => Mql.ToNullableDouble(x.BinaryProperty, ByteOrder.LittleEndian, null)); - - var exception = Record.Exception(() => Translate(collection, queryable)); - Assert.IsType(exception); - Assert.Equal("The 'options' argument cannot be null", exception.Message); - } - - [Theory] - [InlineData(2, ByteOrder.BigEndian, 0, "MongoCommandException")] - [InlineData(3, ByteOrder.LittleEndian, -0.5, null)] - [InlineData(5, ByteOrder.BigEndian, -2.5, null)] - public void MongoDBFunctions_ToDoubleFromBsonBinaryData_should_work(int id, ByteOrder byteOrder, double expectedResult, string expectedException) - { - RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); - - var collection = Fixture.Collection; - var queryable = collection.AsQueryable() - .Where(x => x.Id == id) - .Select(x => Mql.ToDouble(x.BinaryProperty, byteOrder)); - - var expectedStages = - new[] - { - $"{{ $match : {{ _id : {id} }} }}", - $"{{ $project: {{ _v : {{ $convert : {{ input : '$BinaryProperty', to : 'double', {ByteOrderToString(byteOrder)} }} }}, _id : 0 }} }}", - }; - - AssertOutcome(collection, queryable, expectedStages, expectedResult, expectedException); - } - - [Theory] - [InlineData(0, ByteOrder.BigEndian, null, null)] - [InlineData(2, ByteOrder.BigEndian, null, "MongoCommandException")] - [InlineData(3, ByteOrder.LittleEndian, -0.5, null)] - [InlineData(5, ByteOrder.BigEndian, -2.5, null)] - public void MongoDBFunctions_ToNullableDoubleFromBsonBinaryData_should_work(int id, ByteOrder byteOrder, double? expectedResult, string expectedException) - { - RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); - - var collection = Fixture.Collection; - var queryable = collection.AsQueryable() - .Where(x => x.Id == id) - .Select(x => Mql.ToNullableDouble(x.BinaryProperty, byteOrder)); - - var expectedStages = - new[] - { - $"{{ $match : {{ _id : {id} }} }}", - $"{{ $project: {{ _v : {{ $convert : {{ input : '$BinaryProperty', to : 'double', {ByteOrderToString(byteOrder)} }} }}, _id : 0 }} }}", - }; - - AssertOutcome(collection, queryable, expectedStages, expectedResult, expectedException); - } - - [Theory] - [InlineData(2, ByteOrder.LittleEndian, 15.2, true, 15.2, true, 22.3)] - [InlineData(0, ByteOrder.LittleEndian, 22.3, true, 15.2, true, 22.3)] - [InlineData(2, ByteOrder.BigEndian, 15.2,true, 15.2, true,22.3)] - [InlineData(0, ByteOrder.BigEndian, 22.3, true, 15.2, true, 22.3)] - [InlineData(2, ByteOrder.LittleEndian, 0, true, 0, true, 22.3)] - [InlineData(0, ByteOrder.LittleEndian, 0, true, 15.2, true, 0)] - [InlineData(0, ByteOrder.LittleEndian, 0, false, 15.2, true, 0)] - public void MongoDBFunctions_ToDoubleFromBsonBinaryDataWithOptions_should_work(int id, ByteOrder byteOrder, double expectedResult, bool setOnError, double onError, bool setOnNull, double onNull) - { - RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); - - var options = new ConvertOptions(); - if (setOnError) options.OnError = onError; - if (setOnNull) options.OnNull = onNull; - - var collection = Fixture.Collection; - var queryable = collection.AsQueryable() - .Where(x => x.Id == id) - .Select(x => Mql.ToDouble(x.BinaryProperty, byteOrder, options)); - - var onErrorStr = setOnError ? $"onError: {Format(onError)}," : ""; - var onNullStr = setOnNull ? $"onNull: {Format(onNull)}," : ""; - - var expectedStages = - new[] - { - $"{{ $match : {{ _id : {id} }} }}", - $"{{ $project: {{ _v : {{ $convert : {{ input : '$BinaryProperty', to : 'double', {onErrorStr} {onNullStr} {ByteOrderToString(byteOrder)}}} }}, _id : 0 }} }}", - }; - - AssertOutcome(collection, queryable, expectedStages, expectedResult); - } - - [Theory] - [InlineData(2, ByteOrder.LittleEndian, 15.2, true, 15.2, true, 22.3)] - [InlineData(0, ByteOrder.LittleEndian, 22.3, true, 15.2, true, 22.3)] - [InlineData(2, ByteOrder.BigEndian, 15.2,true, 15.2, true,22.3)] - [InlineData(0, ByteOrder.BigEndian, 22.3, true, 15.2, true, 22.3)] - [InlineData(2, ByteOrder.LittleEndian, null, true, null, true, 22.3)] - [InlineData(0, ByteOrder.LittleEndian, null, true, 15.2, true, null)] - [InlineData(2, ByteOrder.LittleEndian, null, true, null, false, 22.3)] - [InlineData(0, ByteOrder.LittleEndian, null, false, 15.2, true, null)] - public void MongoDBFunctions_ToNullableDoubleFromBsonBinaryDataWithOptions_should_work(int id, ByteOrder byteOrder, double? expectedResult, bool setOnError, double? onError, bool setOnNull, double? onNull) - { - RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); - - var options = new ConvertOptions(); - if (setOnError) options.OnError = onError; - if (setOnNull) options.OnNull = onNull; - - var collection = Fixture.Collection; - var queryable = collection.AsQueryable() - .Where(x => x.Id == id) - .Select(x => Mql.ToNullableDouble(x.BinaryProperty, byteOrder, options)); - - var onErrorStr = setOnError ? $"onError: {Format(onError)}," : ""; - var onNullStr = setOnNull ? $"onNull: {Format(onNull)}," : ""; - - var expectedStages = - new[] - { - $"{{ $match : {{ _id : {id} }} }}", - $"{{ $project: {{ _v : {{ $convert : {{ input : '$BinaryProperty', to : 'double', {onErrorStr} {onNullStr} {ByteOrderToString(byteOrder)}}} }}, _id : 0 }} }}", - }; - - AssertOutcome(collection, queryable, expectedStages, expectedResult); - } - - // To Int - - [Fact] - public void MongoDBFunctions_ToIntFromBsonBinaryData_when_onNull_is_not_set_should_throw() - { - RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); - - var options = new ConvertOptions { OnError = 22 }; - - var collection = Fixture.Collection; - var queryable = collection.AsQueryable() - .Where(x => x.Id == 1) - .Select(x => Mql.ToInt(x.BinaryProperty, ByteOrder.LittleEndian, options)); - - var exception = Record.Exception(() => Translate(collection, queryable)); - Assert.IsType(exception); - Assert.Equal("When converting to a non-nullable type, you need to set 'onNull'", exception.Message); - } - - [Fact] - public void MongoDBFunctions_ToIntFromBsonBinaryData_when_options_is_null_should_throw() - { - RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); - - var collection = Fixture.Collection; - var queryable = collection.AsQueryable() - .Where(x => x.Id == 1) - .Select(x => Mql.ToInt(x.BinaryProperty, ByteOrder.LittleEndian, null)); - - var exception = Record.Exception(() => Translate(collection, queryable)); - Assert.IsType(exception); - Assert.Equal("The 'options' argument cannot be null", exception.Message); - } - - [Fact] - public void MongoDBFunctions_ToNullableIntFromBsonBinaryData_when_options_is_null_should_throw() - { - RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); - - var collection = Fixture.Collection; - var queryable = collection.AsQueryable() - .Where(x => x.Id == 1) - .Select(x => Mql.ToNullableInt(x.BinaryProperty, ByteOrder.LittleEndian, null)); - - var exception = Record.Exception(() => Translate(collection, queryable)); - Assert.IsType(exception); - Assert.Equal("The 'options' argument cannot be null", exception.Message); - } - - [Theory] - [InlineData(2, ByteOrder.LittleEndian, 0, "MongoCommandException")] - [InlineData(4, ByteOrder.LittleEndian, 674, null)] - [InlineData(6, ByteOrder.BigEndian, 42, null)] - public void MongoDBFunctions_ToIntFromBsonBinaryData_should_work(int id, ByteOrder byteOrder, int expectedResult, string expectedException) - { - RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); - - var collection = Fixture.Collection; - var queryable = collection.AsQueryable() - .Where(x => x.Id == id) - .Select(x => Mql.ToInt(x.BinaryProperty, byteOrder)); - - var expectedStages = - new[] - { - $"{{ $match : {{ _id : {id} }} }}", - $"{{ $project: {{ _v : {{ $convert : {{ input : '$BinaryProperty', to : 'int', {ByteOrderToString(byteOrder)} }} }}, _id : 0 }} }}", - }; - - AssertOutcome(collection, queryable, expectedStages, expectedResult, expectedException); - } - - [Theory] - [InlineData(0, ByteOrder.BigEndian, null, null)] - [InlineData(2, ByteOrder.LittleEndian, null, "MongoCommandException")] - [InlineData(4, ByteOrder.LittleEndian, 674, null)] - [InlineData(6, ByteOrder.BigEndian, 42, null)] - public void MongoDBFunctions_ToNullableIntFromBsonBinaryData_should_work(int id, ByteOrder byteOrder, int? expectedResult, string expectedException) - { - RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); - - var collection = Fixture.Collection; - var queryable = collection.AsQueryable() - .Where(x => x.Id == id) - .Select(x => Mql.ToNullableInt(x.BinaryProperty, byteOrder)); - - var expectedStages = - new[] - { - $"{{ $match : {{ _id : {id} }} }}", - $"{{ $project: {{ _v : {{ $convert : {{ input : '$BinaryProperty', to : 'int', {ByteOrderToString(byteOrder)} }} }}, _id : 0 }} }}", - }; - - AssertOutcome(collection, queryable, expectedStages, expectedResult, expectedException); - } - - [Theory] - [InlineData(2, ByteOrder.LittleEndian, 15, true, 15, true, 22)] - [InlineData(0, ByteOrder.LittleEndian, 22, true, 15, true, 22)] - [InlineData(2, ByteOrder.BigEndian, 15, true, 15, true, 22)] - [InlineData(0, ByteOrder.BigEndian, 22, true, 15, true, 22)] - [InlineData(2, ByteOrder.LittleEndian, 0, true, 0, true, 22)] - [InlineData(0, ByteOrder.LittleEndian, 0, true, 15, true, 0)] - [InlineData(0, ByteOrder.LittleEndian, 0, false, 15, true, 0)] - public void MongoDBFunctions_ToIntFromBsonBinaryDataWithOptions_should_work(int id, ByteOrder byteOrder, int expectedResult, bool setOnError, int onError, bool setOnNull, int onNull) - { - RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); - - var options = new ConvertOptions(); - if (setOnError) options.OnError = onError; - if (setOnNull) options.OnNull = onNull; - - var collection = Fixture.Collection; - var queryable = collection.AsQueryable() - .Where(x => x.Id == id) - .Select(x => Mql.ToInt(x.BinaryProperty, byteOrder, options)); - - var onErrorStr = setOnError ? $"onError: {Format(onError)}," : ""; - var onNullStr = setOnNull ? $"onNull: {Format(onNull)}," : ""; - - var expectedStages = - new[] - { - $"{{ $match : {{ _id : {id} }} }}", - $"{{ $project: {{ _v : {{ $convert : {{ input : '$BinaryProperty', to : 'int', {onErrorStr} {onNullStr} {ByteOrderToString(byteOrder)} }} }}, _id : 0 }} }}", - }; - - AssertOutcome(collection, queryable, expectedStages, expectedResult); - } - - [Theory] - [InlineData(2, ByteOrder.LittleEndian, 15, true, 15, true, 22)] - [InlineData(0, ByteOrder.LittleEndian, 22, true, 15, true, 22)] - [InlineData(2, ByteOrder.BigEndian, 15, true, 15, true, 22)] - [InlineData(0, ByteOrder.BigEndian, 22, true, 15, true, 22)] - [InlineData(2, ByteOrder.LittleEndian, null, true, null, true, 22)] - [InlineData(0, ByteOrder.LittleEndian, null, true, 15, true, null)] - [InlineData(2, ByteOrder.LittleEndian, null, true, null, false, 22)] - [InlineData(0, ByteOrder.LittleEndian, null, false, 15, true, null)] - public void MongoDBFunctions_ToNullableIntFromBsonBinaryDataWithOptions_should_work(int id, ByteOrder byteOrder, int? expectedResult, bool setOnError, int? onError, bool setOnNull, int? onNull) - { - RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); - - var options = new ConvertOptions(); - if (setOnError) options.OnError = onError; - if (setOnNull) options.OnNull = onNull; - - var collection = Fixture.Collection; - var queryable = collection.AsQueryable() - .Where(x => x.Id == id) - .Select(x => Mql.ToNullableInt(x.BinaryProperty, byteOrder, options)); - - var onErrorStr = setOnError ? $"onError: {Format(onError)}," : ""; - var onNullStr = setOnNull ? $"onNull: {Format(onNull)}," : ""; - - var expectedStages = - new[] - { - $"{{ $match : {{ _id : {id} }} }}", - $"{{ $project: {{ _v : {{ $convert : {{ input : '$BinaryProperty', to : 'int', {onErrorStr} {onNullStr} {ByteOrderToString(byteOrder)} }} }}, _id : 0 }} }}", - }; - - AssertOutcome(collection, queryable, expectedStages, expectedResult); - } - - // To Long - - [Fact] - public void MongoDBFunctions_ToLongFromBsonBinaryData_when_onNull_is_not_set_should_throw() - { - RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); - - var options = new ConvertOptions { OnError = 22 }; - - var collection = Fixture.Collection; - var queryable = collection.AsQueryable() - .Where(x => x.Id == 1) - .Select(x => Mql.ToLong(x.BinaryProperty, ByteOrder.LittleEndian, options)); - - var exception = Record.Exception(() => Translate(collection, queryable)); - Assert.IsType(exception); - Assert.Equal("When converting to a non-nullable type, you need to set 'onNull'", exception.Message); - } - - [Fact] - public void MongoDBFunctions_ToLongFromBsonBinaryData_when_options_is_null_should_throw() - { - RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); - - var collection = Fixture.Collection; - var queryable = collection.AsQueryable() - .Where(x => x.Id == 1) - .Select(x => Mql.ToLong(x.BinaryProperty, ByteOrder.LittleEndian, null)); - - var exception = Record.Exception(() => Translate(collection, queryable)); - Assert.IsType(exception); - Assert.Equal("The 'options' argument cannot be null", exception.Message); - } - - [Fact] - public void MongoDBFunctions_ToNullableLongFromBsonBinaryData_when_options_is_null_should_throw() - { - RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); - - var collection = Fixture.Collection; - var queryable = collection.AsQueryable() - .Where(x => x.Id == 1) - .Select(x => Mql.ToNullableLong(x.BinaryProperty, ByteOrder.LittleEndian, null)); - - var exception = Record.Exception(() => Translate(collection, queryable)); - Assert.IsType(exception); - Assert.Equal("The 'options' argument cannot be null", exception.Message); - } - - [Theory] - [InlineData(2, ByteOrder.LittleEndian, 0, "MongoCommandException")] - [InlineData(4, ByteOrder.LittleEndian, (long)674, null)] - [InlineData(6, ByteOrder.BigEndian, (long)42, null)] - public void MongoDBFunctions_ToLongFromBsonBinaryData_should_work(int id, ByteOrder byteOrder, long expectedResult, string expectedException) - { - RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); - - var collection = Fixture.Collection; - var queryable = collection.AsQueryable() - .Where(x => x.Id == id) - .Select(x => Mql.ToLong(x.BinaryProperty, byteOrder)); - - var expectedStages = - new[] - { - $"{{ $match : {{ _id : {id} }} }}", - $"{{ $project: {{ _v : {{ $convert : {{ input : '$BinaryProperty', to : 'long', {ByteOrderToString(byteOrder)} }} }}, _id : 0 }} }}", - }; - - AssertOutcome(collection, queryable, expectedStages, expectedResult, expectedException); - } - - [Theory] - [InlineData(0, ByteOrder.BigEndian, null, null)] - [InlineData(2, ByteOrder.LittleEndian, null, "MongoCommandException")] - [InlineData(4, ByteOrder.LittleEndian, (long)674, null)] - [InlineData(6, ByteOrder.BigEndian, (long)42, null)] - public void MongoDBFunctions_ToNullableLongFromBsonBinaryData_should_work(int id, ByteOrder byteOrder, long? expectedResult, string expectedException) - { - RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); - - var collection = Fixture.Collection; - var queryable = collection.AsQueryable() - .Where(x => x.Id == id) - .Select(x => Mql.ToNullableLong(x.BinaryProperty, byteOrder)); - - var expectedStages = - new[] - { - $"{{ $match : {{ _id : {id} }} }}", - $"{{ $project: {{ _v : {{ $convert : {{ input : '$BinaryProperty', to : 'long', {ByteOrderToString(byteOrder)} }} }}, _id : 0 }} }}", - }; - - AssertOutcome(collection, queryable, expectedStages, expectedResult, expectedException); - } - - [Theory] - [InlineData(2, ByteOrder.LittleEndian, (long)15, true, (long)15, true, (long)22)] - [InlineData(0, ByteOrder.LittleEndian, (long)22, true, (long)15, true, (long)22)] - [InlineData(2, ByteOrder.BigEndian, (long)15, true, (long)15, true, (long)22)] - [InlineData(0, ByteOrder.BigEndian, (long)22, true, (long)15, true, (long)22)] - [InlineData(2, ByteOrder.LittleEndian, 0, true, 0, true, (long)22)] - [InlineData(0, ByteOrder.LittleEndian, 0, true, (long)15, true, 0)] - [InlineData(0, ByteOrder.LittleEndian, 0, false, (long)15, true, 0)] - public void MongoDBFunctions_ToLongFromBsonBinaryDataWithOptions_should_work(int id, ByteOrder byteOrder, long expectedResult, bool setOnError, long onError, bool setOnNull, long onNull) - { - RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); - - var options = new ConvertOptions(); - if (setOnError) options.OnError = onError; - if (setOnNull) options.OnNull = onNull; - - var collection = Fixture.Collection; - var queryable = collection.AsQueryable() - .Where(x => x.Id == id) - .Select(x => Mql.ToLong(x.BinaryProperty, byteOrder, options)); - - var onErrorStr = setOnError ? $"onError: {Format(onError)}," : ""; - var onNullStr = setOnNull ? $"onNull: {Format(onNull)}," : ""; - - var expectedStages = - new[] - { - $"{{ $match : {{ _id : {id} }} }}", - $"{{ $project: {{ _v : {{ $convert : {{ input : '$BinaryProperty', to : 'long', {onErrorStr} {onNullStr} {ByteOrderToString(byteOrder)} }} }}, _id : 0 }} }}", - }; - - AssertOutcome(collection, queryable, expectedStages, expectedResult); - } - - [Theory] - [InlineData(2, ByteOrder.LittleEndian, (long)15, true, (long)15, true, (long)22)] - [InlineData(0, ByteOrder.LittleEndian, (long)22, true, (long)15, true, (long)22)] - [InlineData(2, ByteOrder.BigEndian, (long)15, true, (long)15, true, (long)22)] - [InlineData(0, ByteOrder.BigEndian, (long)22, true, (long)15, true, (long)22)] - [InlineData(2, ByteOrder.LittleEndian, null, true, null, true, (long)22)] - [InlineData(0, ByteOrder.LittleEndian, null, true, (long)15, true, null)] - [InlineData(2, ByteOrder.LittleEndian, null, true, null, false, (long)22)] - [InlineData(0, ByteOrder.LittleEndian, null, false, (long)15, true, null)] - public void MongoDBFunctions_ToNullableLongFromBsonBinaryDataWithOptions_should_work(int id, ByteOrder byteOrder, long? expectedResult, bool setOnError, long? onError, bool setOnNull, long? onNull) - { - RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); - - var options = new ConvertOptions(); - if (setOnError) options.OnError = onError; - if (setOnNull) options.OnNull = onNull; - - var collection = Fixture.Collection; - var queryable = collection.AsQueryable() - .Where(x => x.Id == id) - .Select(x => Mql.ToNullableLong(x.BinaryProperty, byteOrder, options)); - - var onErrorStr = setOnError ? $"onError: {Format(onError)}," : ""; - var onNullStr = setOnNull ? $"onNull: {Format(onNull)}," : ""; - - var expectedStages = - new[] - { - $"{{ $match : {{ _id : {id} }} }}", - $"{{ $project: {{ _v : {{ $convert : {{ input : '$BinaryProperty', to : 'long', {onErrorStr} {onNullStr} {ByteOrderToString(byteOrder)} }} }}, _id : 0 }} }}", - }; - - AssertOutcome(collection, queryable, expectedStages, expectedResult); - } - - // To String - - [Fact] - public void MongoDBFunctions_ToStringFromBsonBinaryData_when_options_is_null_should_throw() - { - RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); - - var collection = Fixture.Collection; - var queryable = collection.AsQueryable() - .Where(x => x.Id == 1) - .Select(x => Mql.ToString(x.BinaryProperty, "test", null)); - - var exception = Record.Exception(() => Translate(collection, queryable)); - Assert.IsType(exception); - Assert.Equal("The 'options' argument cannot be null", exception.Message); - } - - [Theory] - [InlineData(2, "867dee52-c331-484e-92d1-c56479b8e67e", null)] - [InlineData(1, null, "MongoCommandException")] - public void MongoDBFunctions_ToStringFromBsonBinaryData_should_work(int id, string expectedResult, string expectedException) - { - RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromString); - - var collection = Fixture.Collection; - var queryable = collection.AsQueryable() - .Where(x => x.Id == id) - .Select(x => Mql.ToString(x.BinaryProperty, "uuid")); - - var expectedStages = - new[] - { - $"{{ $match : {{ _id : {id} }} }}", - """{"$project": { "_v" : { "$convert" : { "input" : "$BinaryProperty", "to" : "string", "format" : "uuid" } }, "_id" : 0 }}""", - }; - - AssertOutcome(collection, queryable, expectedStages, expectedResult, expectedException); - } - - [Theory] - [InlineData(0, "onNull", true, "onError", true, "onNull")] - [InlineData(1, "onError", true, "onError", true, "onNull")] - [InlineData(0, null, true, "onError", true, null)] - [InlineData(1, null, true, null, true, "onNull")] - [InlineData(0, null, false, "onError", true, null)] - [InlineData(1, null, true, null, false, "onNull")] - public void MongoDBFunctions_ToStringFromBsonBinaryDataWithOptions_should_work(int id, string expectedResult, bool setOnError, string onError, bool setOnNull, string onNull) - { - RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromString); - - var options = new ConvertOptions(); - if (setOnError) options.OnError = onError; - if (setOnNull) options.OnNull = onNull; - - var collection = Fixture.Collection; - var queryable = collection.AsQueryable() - .Where(x => x.Id == id) - .Select(x => Mql.ToString(x.BinaryProperty, "uuid", options)); - - var onErrorStr = setOnError ? $"onError: {Format(onError)}," : ""; - var onNullStr = setOnNull ? $"onNull: {Format(onNull)}," : ""; - - var expectedStages = - new[] - { - $"{{ $match : {{ _id : {id} }} }}", - $"{{ $project: {{ _v : {{ $convert : {{ input : '$BinaryProperty', to : 'string', {onErrorStr} {onNullStr} format : 'uuid' }} }}, _id : 0 }} }}", - }; - - AssertOutcome(collection, queryable, expectedStages, expectedResult); - } - private void AssertOutcome(IMongoCollection collection, IQueryable queryable, string[] expectedStages, From 6829b66fded9a077c8eaa615eb4a74436b2563e3 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Tue, 1 Apr 2025 14:04:24 +0200 Subject: [PATCH 51/82] Small correction --- .../ConvertMethodToAggregationExpressionTranslatorTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs index 5d9d4e78371..74fab5e6579 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs @@ -37,7 +37,7 @@ public ConvertMethodToAggregationExpressionTranslatorTests(ClassFixture fixture) [Theory] [InlineData(3, ByteOrder.LittleEndian,"AAAAAAAA4L8=", null)] [InlineData(5, ByteOrder.BigEndian, "wAQAAAAAAAA=", null )] - [InlineData(10, ByteOrder.BigEndian, null, "MongoCommandException")] + [InlineData(10, ByteOrder.BigEndian, null, "MongoCommandException")] //TODO This one does not work public void MongoDBFunctions_ToBsonBinaryDataFromDouble_should_work(int id, ByteOrder byteOrder, string expectedBase64, string expectedException) { RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); @@ -54,7 +54,7 @@ public void MongoDBFunctions_ToBsonBinaryDataFromDouble_should_work(int id, Byte $"{{ $project: {{ _v : {{ $convert : {{ input : '$DoubleProperty', to : {{ type: 'binData', subtype: 0 }}, {ByteOrderToString(byteOrder)} }} }}, _id : 0 }} }}", }; - var expectedResult = new BsonBinaryData(Convert.FromBase64String(expectedBase64)); + var expectedResult = expectedBase64 == null ? default : new BsonBinaryData(Convert.FromBase64String(expectedBase64)); AssertOutcome(collection, queryable, expectedStages, expectedResult, expectedException); } From b237d0def8f3e4e2ea5e889c4552470c098b568d Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Tue, 1 Apr 2025 14:22:46 +0200 Subject: [PATCH 52/82] Test improvement --- ...dToAggregationExpressionTranslatorTests.cs | 29 +++++++++++++++++-- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs index 74fab5e6579..3f7a1ff0930 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs @@ -33,12 +33,11 @@ public ConvertMethodToAggregationExpressionTranslatorTests(ClassFixture fixture) { } - [Theory] [InlineData(3, ByteOrder.LittleEndian,"AAAAAAAA4L8=", null)] [InlineData(5, ByteOrder.BigEndian, "wAQAAAAAAAA=", null )] - [InlineData(10, ByteOrder.BigEndian, null, "MongoCommandException")] //TODO This one does not work - public void MongoDBFunctions_ToBsonBinaryDataFromDouble_should_work(int id, ByteOrder byteOrder, string expectedBase64, string expectedException) + [InlineData(10, ByteOrder.BigEndian, null, "MongoCommandException")] + public void Test1(int id, ByteOrder byteOrder, string expectedBase64, string expectedException) { RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); @@ -58,6 +57,30 @@ public void MongoDBFunctions_ToBsonBinaryDataFromDouble_should_work(int id, Byte AssertOutcome(collection, queryable, expectedStages, expectedResult, expectedException); } + [Theory] + [InlineData(3, ByteOrder.LittleEndian,"AAAAAAAA4L8=", null)] + [InlineData(5, ByteOrder.BigEndian, "wAQAAAAAAAA=", null )] + [InlineData(10, ByteOrder.BigEndian, null, "MongoCommandException")] + public void Test2(int id, ByteOrder byteOrder, string expectedBase64, string expectedException) + { + RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); + + var collection = Fixture.Collection; + var queryable = collection.AsQueryable() + .Where(x => x.Id == id) + .Select(x => Mql.Convert(x.DoubleProperty, new ConvertOptions { ByteOrder = byteOrder, SubType = BsonBinarySubType.Binary })); + + var expectedStages = + new[] + { + $"{{ $match : {{ _id : {id} }} }}", + $"{{ $project: {{ _v : {{ $convert : {{ input : '$DoubleProperty', to : {{ type: 'binData', subtype: 0 }}, {ByteOrderToString(byteOrder)} }} }}, _id : 0 }} }}", + }; + + var expectedResult = expectedBase64 == null ? default : Convert.FromBase64String(expectedBase64); + AssertOutcome(collection, queryable, expectedStages, expectedResult, expectedException); + } + private void AssertOutcome(IMongoCollection collection, IQueryable queryable, string[] expectedStages, From 555bbd9f3cf66354655d534eb5c15d1afd087fc3 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Tue, 1 Apr 2025 14:33:42 +0200 Subject: [PATCH 53/82] Added tests --- ...dToAggregationExpressionTranslatorTests.cs | 62 ++++++++++++++----- 1 file changed, 46 insertions(+), 16 deletions(-) diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs index 3f7a1ff0930..40c9159fbb5 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs @@ -15,7 +15,6 @@ using System; using System.Collections.Generic; -using System.Globalization; using System.Linq; using MongoDB.Bson; using MongoDB.Driver.Core.Misc; @@ -81,6 +80,52 @@ public void Test2(int id, ByteOrder byteOrder, string expectedBase64, string exp AssertOutcome(collection, queryable, expectedStages, expectedResult, expectedException); } + [Theory] + [InlineData(0, ByteOrder.BigEndian, null, null)] + [InlineData(2, ByteOrder.LittleEndian, null, "MongoCommandException")] + [InlineData(4, ByteOrder.LittleEndian, 674, null)] + [InlineData(6, ByteOrder.BigEndian, 42, null)] + public void Test3(int id, ByteOrder byteOrder, int? expectedResult, string expectedException) + { + RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); + + var collection = Fixture.Collection; + var queryable = collection.AsQueryable() + .Where(x => x.Id == id) + .Select(x => Mql.Convert(x.BinaryProperty, new ConvertOptions {ByteOrder = byteOrder})); + + var expectedStages = + new[] + { + $"{{ $match : {{ _id : {id} }} }}", + $"{{ $project: {{ _v : {{ $convert : {{ input : '$BinaryProperty', to : 'int', {ByteOrderToString(byteOrder)} }} }}, _id : 0 }} }}", + }; + + AssertOutcome(collection, queryable, expectedStages, expectedResult, expectedException); + } + + [Theory] + [InlineData(4, ByteOrder.LittleEndian, 674, null)] + [InlineData(6, ByteOrder.BigEndian, 42, null)] + public void Test4(int id, ByteOrder byteOrder, int expectedResult, string expectedException) + { + RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); + + var collection = Fixture.Collection; + var queryable = collection.AsQueryable() + .Where(x => x.Id == id) + .Select(x => Mql.Convert(x.BinaryProperty, new ConvertOptions {ByteOrder = byteOrder})); + + var expectedStages = + new[] + { + $"{{ $match : {{ _id : {id} }} }}", + $"{{ $project: {{ _v : {{ $convert : {{ input : '$BinaryProperty', to : 'int', {ByteOrderToString(byteOrder)} }} }}, _id : 0 }} }}", + }; + + AssertOutcome(collection, queryable, expectedStages, expectedResult, expectedException); + } + private void AssertOutcome(IMongoCollection collection, IQueryable queryable, string[] expectedStages, @@ -117,21 +162,6 @@ private string ByteOrderToString(ByteOrder byteOrder) return $"byteOrder: '{byteOrderString}'"; } - private static string Format(double? value) => - value?.ToString(NumberFormatInfo.InvariantInfo) ?? "null"; - - private static string Format(int? value) => - value?.ToString(NumberFormatInfo.InvariantInfo) ?? "null"; - - private static string Format(long? value) => - value?.ToString(NumberFormatInfo.InvariantInfo) ?? "null"; - - private static string Format(string value) => - value is null ? "null" : $"'{value}'"; - - private static string FormatBase64(string base64String) => - base64String is null ? "null" :$"BinData(0, '{base64String}')"; - public sealed class ClassFixture : MongoCollectionFixture { protected override IEnumerable InitialData => From fd9626c061a9442eef0c00fa3bc6dcbebec379f3 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Wed, 2 Apr 2025 11:16:10 +0200 Subject: [PATCH 54/82] Nits correction --- .../Ast/Expressions/AstConvertExpression.cs | 15 ++++--- ...essionToAggregationExpressionTranslator.cs | 4 +- ...dToAggregationExpressionTranslatorTests.cs | 45 ++++++++++++++++++- 3 files changed, 53 insertions(+), 11 deletions(-) diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstConvertExpression.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstConvertExpression.cs index 14b73f5ea6e..b1b1497bab2 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstConvertExpression.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstConvertExpression.cs @@ -22,8 +22,8 @@ namespace MongoDB.Driver.Linq.Linq3Implementation.Ast.Expressions internal sealed class AstConvertExpression : AstExpression { private readonly ByteOrder? _byteOrder; - private readonly AstExpression _input; private readonly string _format; + private readonly AstExpression _input; private readonly AstExpression _onError; private readonly AstExpression _onNull; private readonly BsonBinarySubType? _subType; @@ -48,8 +48,8 @@ public AstConvertExpression( } public ByteOrder? ByteOrder => _byteOrder; - public AstExpression Input => _input; public string Format => _format; + public AstExpression Input => _input; public override AstNodeType NodeType => AstNodeType.ConvertExpression; public AstExpression OnError => _onError; public AstExpression OnNull => _onNull; @@ -71,14 +71,15 @@ public override BsonValue Render() { "to", _to.Render(), _subType == null }, { "to", () => new BsonDocument { - {"type", _to.Render() }, - {"subtype", (int)_subType!.Value}, - }, _subType != null + { "type", _to.Render() }, + { "subtype", (int)_subType!.Value}, + }, + _subType != null }, { "onError", () => _onError.Render(), _onError != null }, { "onNull", () => _onNull.Render(), _onNull != null }, - { "format", () => _format, _format != null}, - { "byteOrder", () => _byteOrder!.Value.Render(), _byteOrder != null} + { "format", () => _format, _format != null }, + { "byteOrder", () => _byteOrder!.Value.Render(), _byteOrder != null } } } }; diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodCallExpressionToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodCallExpressionToAggregationExpressionTranslator.cs index c052a6c9401..3054156427a 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodCallExpressionToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodCallExpressionToAggregationExpressionTranslator.cs @@ -39,6 +39,7 @@ public static TranslatedExpression Translate(TranslationContext context, MethodC case "Contains": return ContainsMethodToAggregationExpressionTranslator.Translate(context, expression); case "ContainsKey": return ContainsKeyMethodToAggregationExpressionTranslator.Translate(context, expression); case "ContainsValue": return ContainsValueMethodToAggregationExpressionTranslator.Translate(context, expression); + case "Convert": return ConvertMethodToAggregationExpressionTranslator.Translate(context, expression); case "CovariancePopulation": return CovariancePopulationMethodToAggregationExpressionTranslator.Translate(context, expression); case "CovarianceSample": return CovarianceSampleMethodToAggregationExpressionTranslator.Translate(context, expression); case "Create": return CreateMethodToAggregationExpressionTranslator.Translate(context, expression); @@ -138,9 +139,6 @@ public static TranslatedExpression Translate(TranslationContext context, MethodC case "LongCount": return CountMethodToAggregationExpressionTranslator.Translate(context, expression); - case "Convert": - return ConvertMethodToAggregationExpressionTranslator.Translate(context, expression); - case "ElementAt": case "ElementAtOrDefault": return ElementAtMethodToAggregationExpressionTranslator.Translate(context, expression); diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs index 40c9159fbb5..c23312d0946 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs @@ -126,6 +126,48 @@ public void Test4(int id, ByteOrder byteOrder, int expectedResult, string expect AssertOutcome(collection, queryable, expectedStages, expectedResult, expectedException); } + [Theory] + [InlineData(0, ByteOrder.BigEndian, 50, null)] + public void Test5(int id, ByteOrder byteOrder, int? expectedResult, string expectedException) + { + RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); + + var collection = Fixture.Collection; + var queryable = collection.AsQueryable() + .Where(x => x.Id == id) + .Select(x => Mql.Convert(x.BinaryProperty, new ConvertOptions {ByteOrder = byteOrder, OnNull = 50})); + + var expectedStages = + new[] + { + $"{{ $match : {{ _id : {id} }} }}", + $"{{ $project: {{ _v : {{ $convert : {{ input : '$BinaryProperty', to : 'int', 'onNull': 22, {ByteOrderToString(byteOrder)} }} }}, _id : 0 }} }}", + }; + + AssertOutcome(collection, queryable, expectedStages, expectedResult, expectedException); + } + + [Theory] + [InlineData(0, ByteOrder.BigEndian, 22, null)] + public void Test6(int id, ByteOrder byteOrder, int? expectedResult, string expectedException) + { + RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); + + var collection = Fixture.Collection; + var queryable = collection.AsQueryable() + .Where(x => x.Id == id) + .Select(x => Mql.Convert(x.BinaryProperty, new ConvertOptions {ByteOrder = byteOrder, OnNull = x.ExtraIntProperty})); + + var expectedStages = + new[] + { + $"{{ $match : {{ _id : {id} }} }}", + $"{{ $project: {{ _v : {{ $convert : {{ input : '$BinaryProperty', to : 'int', 'onNull': 22, {ByteOrderToString(byteOrder)} }} }}, _id : 0 }} }}", + }; + + AssertOutcome(collection, queryable, expectedStages, expectedResult, expectedException); + } + private void AssertOutcome(IMongoCollection collection, IQueryable queryable, string[] expectedStages, @@ -166,7 +208,7 @@ public sealed class ClassFixture : MongoCollectionFixture InitialData => [ - BsonDocument.Parse("{ _id : 0 }"), + BsonDocument.Parse("{ _id : 0, ExtraIntProperty : 22 }"), BsonDocument.Parse("{ _id : 1, BinaryProperty : BinData(0, 'ogIAAA==') }"), BsonDocument.Parse("{ _id : 2, BinaryProperty : BinData(4, 'hn3uUsMxSE6S0cVkebjmfg=='), StringProperty: '867dee52-c331-484e-92d1-c56479b8e67e' }"), BsonDocument.Parse("{ _id : 3, BinaryProperty : BinData(0, 'AAAAAAAA4L8='), DoubleProperty: -0.5, NullableDoubleProperty: -0.5 }"), // LittleEndian @@ -189,6 +231,7 @@ public class TestClass public int? NullableIntProperty { get; set; } public long? NullableLongProperty { get; set; } public string StringProperty { get; set; } + public int ExtraIntProperty { get; set; } } } } \ No newline at end of file From 403c02aab6c3da18f7f3252eaaa55d0a30c335fc Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Wed, 2 Apr 2025 11:23:22 +0200 Subject: [PATCH 55/82] Nit corrections --- .../Linq3Implementation/Ast/AstEnumExtensions.cs | 2 +- .../Ast/Expressions/AstExpression.cs | 2 +- ...ConvertMethodToAggregationExpressionTranslator.cs | 2 +- src/MongoDB.Driver/Mql.cs | 12 ++++++------ 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/AstEnumExtensions.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/AstEnumExtensions.cs index 9f24e8e7ff8..740cc624f94 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/AstEnumExtensions.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/AstEnumExtensions.cs @@ -54,7 +54,7 @@ public static string Render(this ByteOrder byteOrder) { ByteOrder.BigEndian => "big", ByteOrder.LittleEndian => "little", - _ => throw new ArgumentException($"Unexpected Mql.ByteOrder: {byteOrder}.", nameof(byteOrder)) + _ => throw new ArgumentException($"Unexpected {nameof(ByteOrder)}: {byteOrder}.", nameof(byteOrder)) }; } } diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstExpression.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstExpression.cs index 8de612f95f1..e9715aca4b1 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstExpression.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstExpression.cs @@ -267,7 +267,7 @@ public static AstExpression Convert( Ensure.IsNotNull(input, nameof(input)); Ensure.IsNotNull(to, nameof(to)); - if (onError == null && onNull == null && subType == null && format == null && byteOrder == null && + if (onError == null && onNull == null && subType == null && format == null && byteOrder == null && //TODO Move to AstSimplifier.cs to is AstConstantExpression toConstantExpression && (toConstantExpression.Value as BsonString)?.Value is { } toValue) { diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs index 0358b59a9f4..c6cbb31eb02 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs @@ -55,7 +55,7 @@ public static TranslatedExpression Translate(TranslationContext context, MethodC return new TranslatedExpression(expression, ast, serializer); } - public static BsonType GetBsonType(Type type) + public static BsonType GetBsonType(Type type) //TODO Do we have this kind of info somewhere else...? { return Type.GetTypeCode(Nullable.GetUnderlyingType(type) ?? type) switch { diff --git a/src/MongoDB.Driver/Mql.cs b/src/MongoDB.Driver/Mql.cs index 5b502f2c970..f0c93baa455 100644 --- a/src/MongoDB.Driver/Mql.cs +++ b/src/MongoDB.Driver/Mql.cs @@ -233,18 +233,18 @@ public BsonBinarySubType? SubType /// Represents the typed options parameter for the conversion methods in the Mql static class. /// This class allows to set 'onError' and 'onNull'. /// - /// The type of 'onError' and 'onNull'. - public class ConvertOptions : ConvertOptions + /// The type of 'onError' and 'onNull'. + public class ConvertOptions : ConvertOptions { - private TResult _onError; + private TTo _onError; private bool _onErrorWasSet; - private TResult _onNull; + private TTo _onNull; private bool _onNullWasSet; /// /// The onError parameter. /// - public TResult OnError + public TTo OnError { get => _onError; set @@ -257,7 +257,7 @@ public TResult OnError /// /// The onNull parameter. /// - public TResult OnNull + public TTo OnNull { get => _onNull; set From 69cf61bb47821e3df3412feaf5ebcd21848de438 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Wed, 2 Apr 2025 11:58:48 +0200 Subject: [PATCH 56/82] Imrpoved --- ...MethodToAggregationExpressionTranslator.cs | 82 ++++++++++++++++--- ...dToAggregationExpressionTranslatorTests.cs | 4 +- 2 files changed, 72 insertions(+), 14 deletions(-) diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs index c6cbb31eb02..3c61dad1ca1 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs @@ -25,33 +25,91 @@ public static TranslatedExpression Translate(TranslationContext context, MethodC throw new ExpressionNotSupportedException(expression); } + var fieldAst = ExpressionToAggregationExpressionTranslator.Translate(context, arguments[0]).Ast; + AstExpression onErrorAst = null; AstExpression onNullAst = null; + BsonBinarySubType? subType = null; + ByteOrder? byteOrder = null; + string format = null; - var fieldAst = ExpressionToAggregationExpressionTranslator.Translate(context, arguments[0]).Ast; - if (arguments[1] is not ConstantExpression constantExpression) + var optionExpression = arguments[1]; + + if (optionExpression is ConstantExpression constantExpression) { - throw new InvalidOperationException("The 'options' argument must be a constant expression"); - } + var options = (ConvertOptions)constantExpression.Value; - var options = (ConvertOptions)constantExpression.Value; + if (options.OnErrorWasSet) + { + onErrorAst = options.GetOnError(); + } - if (options.OnErrorWasSet) - { - onErrorAst = options.GetOnError(); - } + if (options.OnNullWasSet) + { + onNullAst = options.GetOnNull(); + } - if (options.OnNullWasSet) + subType = options.SubType; + format = options.Format; + byteOrder = options.ByteOrder; + } + else if (arguments[1] is MemberInitExpression memberInitExpression) { - onNullAst = options.GetOnNull(); + foreach (var binding in memberInitExpression.Bindings) + { + if (binding is not MemberAssignment memberAssignment) continue; + + var memberName = memberAssignment.Member.Name; + + + if (memberName == "OnError") + { + var translatedExpression = + ExpressionToAggregationExpressionTranslator.Translate(context, memberAssignment.Expression); + onErrorAst = translatedExpression.Ast; + } + else if (memberName == "OnNull") + { + var translatedExpression = + ExpressionToAggregationExpressionTranslator.Translate(context, memberAssignment.Expression); + onNullAst = translatedExpression.Ast; + } + else if (memberName == "Format") + { + if (memberAssignment.Expression is not ConstantExpression formatExpression) + { + throw new ExpressionNotSupportedException(expression); //TODO Improve + } + + format = (string)formatExpression.Value; + } + else if (memberName == "ByteOrder") + { + if (memberAssignment.Expression is not ConstantExpression byteOrderExpression) + { + throw new ExpressionNotSupportedException(expression); //TODO Improve + } + + byteOrder = (ByteOrder)byteOrderExpression.Value; + } + else if (memberName == "SubType") + { + if (memberAssignment.Expression is not ConstantExpression subTypeExpression) + { + throw new ExpressionNotSupportedException(expression); //TODO Improve + } + + subType = (BsonBinarySubType)subTypeExpression.Value; + } + } } var toType = method.GetGenericArguments()[1]; var toBsonType = GetBsonType(toType).Render(); var serializer = BsonSerializer.LookupSerializer(toType); - var ast = AstExpression.Convert(fieldAst, toBsonType, subType: options.SubType, byteOrder: options.ByteOrder, format: options.Format, onError: onErrorAst, onNull: onNullAst); + var ast = AstExpression.Convert(fieldAst, toBsonType, subType: subType, byteOrder: byteOrder, format: format, onError: onErrorAst, onNull: onNullAst); return new TranslatedExpression(expression, ast, serializer); } diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs index c23312d0946..5566c4685c8 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs @@ -141,7 +141,7 @@ public void Test5(int id, ByteOrder byteOrder, int? expectedResult, string expec new[] { $"{{ $match : {{ _id : {id} }} }}", - $"{{ $project: {{ _v : {{ $convert : {{ input : '$BinaryProperty', to : 'int', 'onNull': 22, {ByteOrderToString(byteOrder)} }} }}, _id : 0 }} }}", + $"{{ $project: {{ _v : {{ $convert : {{ input : '$BinaryProperty', to : 'int', 'onNull': 50, {ByteOrderToString(byteOrder)} }} }}, _id : 0 }} }}", }; AssertOutcome(collection, queryable, expectedStages, expectedResult, expectedException); @@ -162,7 +162,7 @@ public void Test6(int id, ByteOrder byteOrder, int? expectedResult, string expec new[] { $"{{ $match : {{ _id : {id} }} }}", - $"{{ $project: {{ _v : {{ $convert : {{ input : '$BinaryProperty', to : 'int', 'onNull': 22, {ByteOrderToString(byteOrder)} }} }}, _id : 0 }} }}", + $"{{ $project: {{ _v : {{ $convert : {{ input : '$BinaryProperty', to : 'int', 'onNull': '$ExtraIntProperty', {ByteOrderToString(byteOrder)} }} }}, _id : 0 }} }}", }; AssertOutcome(collection, queryable, expectedStages, expectedResult, expectedException); From 8e8eea591174620ebd071126f6f5e4526cef5f1b Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Wed, 2 Apr 2025 13:21:00 +0200 Subject: [PATCH 57/82] Small fix --- ...MethodToAggregationExpressionTranslator.cs | 82 ++++++++++--------- 1 file changed, 42 insertions(+), 40 deletions(-) diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs index 3c61dad1ca1..8e03270ae01 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs @@ -1,11 +1,7 @@ using System; -using System.Collections.Generic; -using System.Linq; using System.Linq.Expressions; -using System.Reflection; using MongoDB.Bson; using MongoDB.Bson.Serialization; -using MongoDB.Bson.Serialization.Serializers; using MongoDB.Driver.Linq.Linq3Implementation.Ast; using MongoDB.Driver.Linq.Linq3Implementation.Ast.Expressions; using MongoDB.Driver.Linq.Linq3Implementation.Misc; @@ -25,14 +21,13 @@ public static TranslatedExpression Translate(TranslationContext context, MethodC throw new ExpressionNotSupportedException(expression); } - var fieldAst = ExpressionToAggregationExpressionTranslator.Translate(context, arguments[0]).Ast; - + ByteOrder? byteOrder = null; + string format = null; AstExpression onErrorAst = null; AstExpression onNullAst = null; BsonBinarySubType? subType = null; - ByteOrder? byteOrder = null; - string format = null; + var fieldAst = ExpressionToAggregationExpressionTranslator.Translate(context, arguments[0]).Ast; var optionExpression = arguments[1]; @@ -54,7 +49,7 @@ public static TranslatedExpression Translate(TranslationContext context, MethodC format = options.Format; byteOrder = options.ByteOrder; } - else if (arguments[1] is MemberInitExpression memberInitExpression) + else if (optionExpression is MemberInitExpression memberInitExpression) { foreach (var binding in memberInitExpression.Bindings) { @@ -62,48 +57,55 @@ public static TranslatedExpression Translate(TranslationContext context, MethodC var memberName = memberAssignment.Member.Name; - - if (memberName == "OnError") - { - var translatedExpression = - ExpressionToAggregationExpressionTranslator.Translate(context, memberAssignment.Expression); - onErrorAst = translatedExpression.Ast; - } - else if (memberName == "OnNull") - { - var translatedExpression = - ExpressionToAggregationExpressionTranslator.Translate(context, memberAssignment.Expression); - onNullAst = translatedExpression.Ast; - } - else if (memberName == "Format") + switch (memberName) { - if (memberAssignment.Expression is not ConstantExpression formatExpression) + case "OnError": { - throw new ExpressionNotSupportedException(expression); //TODO Improve + onErrorAst = ExpressionToAggregationExpressionTranslator.Translate(context, memberAssignment.Expression).Ast; + break; } - - format = (string)formatExpression.Value; - } - else if (memberName == "ByteOrder") - { - if (memberAssignment.Expression is not ConstantExpression byteOrderExpression) + case "OnNull": { - throw new ExpressionNotSupportedException(expression); //TODO Improve + onNullAst = ExpressionToAggregationExpressionTranslator.Translate(context, memberAssignment.Expression).Ast; + break; } + case "Format": + { + if (memberAssignment.Expression is not ConstantExpression formatExpression) + { + throw new ExpressionNotSupportedException("The 'format' field must be a constant expression"); //TODO Improve message? + } - byteOrder = (ByteOrder)byteOrderExpression.Value; - } - else if (memberName == "SubType") - { - if (memberAssignment.Expression is not ConstantExpression subTypeExpression) + format = (string)formatExpression.Value; + break; + } + case "ByteOrder": { - throw new ExpressionNotSupportedException(expression); //TODO Improve + if (memberAssignment.Expression is not ConstantExpression byteOrderExpression) + { + throw new ExpressionNotSupportedException("The 'byteOrder' field must be a constant expression"); //TODO Improve message? + } + + byteOrder = (ByteOrder)byteOrderExpression.Value; + break; } + case "SubType": + { + if (memberAssignment.Expression is not ConstantExpression subTypeExpression) + { + throw new ExpressionNotSupportedException("The 'subType' field must be a constant expression"); //TODO Improve message? + } - subType = (BsonBinarySubType)subTypeExpression.Value; + subType = (BsonBinarySubType)subTypeExpression.Value; + break; + } } } } + else + { + throw new ExpressionNotSupportedException("The 'Options' argument can be either a constant expression or a member initialization expression"); //TODO Improve message? + } var toType = method.GetGenericArguments()[1]; var toBsonType = GetBsonType(toType).Render(); @@ -113,7 +115,7 @@ public static TranslatedExpression Translate(TranslationContext context, MethodC return new TranslatedExpression(expression, ast, serializer); } - public static BsonType GetBsonType(Type type) //TODO Do we have this kind of info somewhere else...? + private static BsonType GetBsonType(Type type) //TODO Do we have this kind of info somewhere else...? { return Type.GetTypeCode(Nullable.GetUnderlyingType(type) ?? type) switch { From 6e87398bf001fb93c72e3f40b25fb4cedbb242c4 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Wed, 2 Apr 2025 14:25:21 +0200 Subject: [PATCH 58/82] Tried to move stuff to simplifier --- .../Ast/Expressions/AstExpression.cs | 23 --------------- .../Ast/Optimizers/AstSimplifier.cs | 29 +++++++++++++++++++ 2 files changed, 29 insertions(+), 23 deletions(-) diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstExpression.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstExpression.cs index e9715aca4b1..2b0aecb8c77 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstExpression.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstExpression.cs @@ -267,29 +267,6 @@ public static AstExpression Convert( Ensure.IsNotNull(input, nameof(input)); Ensure.IsNotNull(to, nameof(to)); - if (onError == null && onNull == null && subType == null && format == null && byteOrder == null && //TODO Move to AstSimplifier.cs - to is AstConstantExpression toConstantExpression && - (toConstantExpression.Value as BsonString)?.Value is { } toValue) - { - var unaryOperator = toValue switch - { - "bool" => AstUnaryOperator.ToBool, - "date" => AstUnaryOperator.ToDate, - "decimal" => AstUnaryOperator.ToDecimal, - "double" => AstUnaryOperator.ToDouble, - "int" => AstUnaryOperator.ToInt, - "long" => AstUnaryOperator.ToLong, - "objectId" => AstUnaryOperator.ToObjectId, - "string" => AstUnaryOperator.ToString, - _ => (AstUnaryOperator?)null - }; - - if (unaryOperator.HasValue) - { - return AstExpression.Unary(unaryOperator.Value, input); - } - } - return new AstConvertExpression(input, to, subType, byteOrder, format, onError, onNull); } diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Optimizers/AstSimplifier.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Optimizers/AstSimplifier.cs index 206fd8b308c..43bee247193 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Optimizers/AstSimplifier.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Optimizers/AstSimplifier.cs @@ -107,6 +107,35 @@ static bool OperatorMapsNullToNull(AstUnaryOperator @operator) } } + public override AstNode VisitConvertExpression(AstConvertExpression node) + { + var optimizedNode = (AstConvertExpression)base.VisitConvertExpression(node); + + if (optimizedNode is { OnError: null, OnNull: null, SubType: null, Format: null, ByteOrder: null, To: AstConstantExpression toConstantExpression } && + (toConstantExpression.Value as BsonString)?.Value is { } toValue) + { + var unaryOperator = toValue switch + { + "bool" => AstUnaryOperator.ToBool, + "date" => AstUnaryOperator.ToDate, + "decimal" => AstUnaryOperator.ToDecimal, + "double" => AstUnaryOperator.ToDouble, + "int" => AstUnaryOperator.ToInt, + "long" => AstUnaryOperator.ToLong, + "objectId" => AstUnaryOperator.ToObjectId, + "string" => AstUnaryOperator.ToString, + _ => (AstUnaryOperator?)null + }; + + if (unaryOperator.HasValue) + { + return AstExpression.Unary(unaryOperator.Value, optimizedNode.Input); + } + } + + return optimizedNode; + } + public override AstNode VisitExprFilter(AstExprFilter node) { var optimizedNode = base.VisitExprFilter(node); From fb5509e2a9c74d7781fde2a7d2dea3ae75551f09 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Wed, 2 Apr 2025 14:48:50 +0200 Subject: [PATCH 59/82] Small fixes --- .../Ast/Optimizers/AstSimplifier.cs | 1 + ...MethodToAggregationExpressionTranslator.cs | 38 +++++++++---------- 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Optimizers/AstSimplifier.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Optimizers/AstSimplifier.cs index 43bee247193..750d3beaabd 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Optimizers/AstSimplifier.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Optimizers/AstSimplifier.cs @@ -111,6 +111,7 @@ public override AstNode VisitConvertExpression(AstConvertExpression node) { var optimizedNode = (AstConvertExpression)base.VisitConvertExpression(node); + // If possible, uses $toType ($toInt, $toDouble, ...) operators instead of $convert if (optimizedNode is { OnError: null, OnNull: null, SubType: null, Format: null, ByteOrder: null, To: AstConstantExpression toConstantExpression } && (toConstantExpression.Value as BsonString)?.Value is { } toValue) { diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs index 8e03270ae01..de42eeda466 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs @@ -59,44 +59,44 @@ public static TranslatedExpression Translate(TranslationContext context, MethodC switch (memberName) { - case "OnError": + case nameof(ConvertOptions.ByteOrder): { - onErrorAst = ExpressionToAggregationExpressionTranslator.Translate(context, memberAssignment.Expression).Ast; - break; - } - case "OnNull": - { - onNullAst = ExpressionToAggregationExpressionTranslator.Translate(context, memberAssignment.Expression).Ast; + if (memberAssignment.Expression is not ConstantExpression byteOrderExpression) + { + throw new ExpressionNotSupportedException($"The {nameof(ConvertOptions.ByteOrder)} field must be a constant expression"); //TODO Improve message? + } + + byteOrder = (ByteOrder?)byteOrderExpression.Value; break; } - case "Format": + case nameof(ConvertOptions.Format): { if (memberAssignment.Expression is not ConstantExpression formatExpression) { - throw new ExpressionNotSupportedException("The 'format' field must be a constant expression"); //TODO Improve message? + throw new ExpressionNotSupportedException($"The {nameof(ConvertOptions.Format)} field must be a constant expression"); //TODO Improve message? } format = (string)formatExpression.Value; break; } - case "ByteOrder": + case nameof(ConvertOptions.OnError): { - if (memberAssignment.Expression is not ConstantExpression byteOrderExpression) - { - throw new ExpressionNotSupportedException("The 'byteOrder' field must be a constant expression"); //TODO Improve message? - } - - byteOrder = (ByteOrder)byteOrderExpression.Value; + onErrorAst = ExpressionToAggregationExpressionTranslator.Translate(context, memberAssignment.Expression).Ast; + break; + } + case nameof(ConvertOptions.OnNull): + { + onNullAst = ExpressionToAggregationExpressionTranslator.Translate(context, memberAssignment.Expression).Ast; break; } - case "SubType": + case nameof(ConvertOptions.SubType): { if (memberAssignment.Expression is not ConstantExpression subTypeExpression) { - throw new ExpressionNotSupportedException("The 'subType' field must be a constant expression"); //TODO Improve message? + throw new ExpressionNotSupportedException($"The {nameof(ConvertOptions.SubType)} field must be a constant expression"); //TODO Improve message? } - subType = (BsonBinarySubType)subTypeExpression.Value; + subType = (BsonBinarySubType?)subTypeExpression.Value; break; } } From 3ee0f42b8850aa5ec574e492ac65b1169bdb59b5 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Wed, 2 Apr 2025 15:37:19 +0200 Subject: [PATCH 60/82] Removed extra line --- src/MongoDB.Driver/Mql.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/MongoDB.Driver/Mql.cs b/src/MongoDB.Driver/Mql.cs index f0c93baa455..98b0c839cca 100644 --- a/src/MongoDB.Driver/Mql.cs +++ b/src/MongoDB.Driver/Mql.cs @@ -273,6 +273,5 @@ public TTo OnNull internal override BsonValue GetOnError() => BsonValue.Create(_onError); internal override BsonValue GetOnNull() => BsonValue.Create(_onNull); - } } From 705f5b316e99fd644658d770288e7b0e5bb58c24 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Thu, 3 Apr 2025 09:01:24 +0200 Subject: [PATCH 61/82] Added missing copyright --- ...vertMethodToAggregationExpressionTranslator.cs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs index de42eeda466..fba6049a88e 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs @@ -1,3 +1,18 @@ +/* Copyright 2010-present MongoDB Inc. + * + * 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; using System.Linq.Expressions; using MongoDB.Bson; From 2a0829b603291f56759519724414a9e7447c1466 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Thu, 3 Apr 2025 09:07:39 +0200 Subject: [PATCH 62/82] Restored simplification --- .../Ast/Expressions/AstExpression.cs | 23 ++++++++++++++ .../Ast/Optimizers/AstSimplifier.cs | 30 ------------------- 2 files changed, 23 insertions(+), 30 deletions(-) diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstExpression.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstExpression.cs index 2b0aecb8c77..8de612f95f1 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstExpression.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstExpression.cs @@ -267,6 +267,29 @@ public static AstExpression Convert( Ensure.IsNotNull(input, nameof(input)); Ensure.IsNotNull(to, nameof(to)); + if (onError == null && onNull == null && subType == null && format == null && byteOrder == null && + to is AstConstantExpression toConstantExpression && + (toConstantExpression.Value as BsonString)?.Value is { } toValue) + { + var unaryOperator = toValue switch + { + "bool" => AstUnaryOperator.ToBool, + "date" => AstUnaryOperator.ToDate, + "decimal" => AstUnaryOperator.ToDecimal, + "double" => AstUnaryOperator.ToDouble, + "int" => AstUnaryOperator.ToInt, + "long" => AstUnaryOperator.ToLong, + "objectId" => AstUnaryOperator.ToObjectId, + "string" => AstUnaryOperator.ToString, + _ => (AstUnaryOperator?)null + }; + + if (unaryOperator.HasValue) + { + return AstExpression.Unary(unaryOperator.Value, input); + } + } + return new AstConvertExpression(input, to, subType, byteOrder, format, onError, onNull); } diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Optimizers/AstSimplifier.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Optimizers/AstSimplifier.cs index 750d3beaabd..206fd8b308c 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Optimizers/AstSimplifier.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Optimizers/AstSimplifier.cs @@ -107,36 +107,6 @@ static bool OperatorMapsNullToNull(AstUnaryOperator @operator) } } - public override AstNode VisitConvertExpression(AstConvertExpression node) - { - var optimizedNode = (AstConvertExpression)base.VisitConvertExpression(node); - - // If possible, uses $toType ($toInt, $toDouble, ...) operators instead of $convert - if (optimizedNode is { OnError: null, OnNull: null, SubType: null, Format: null, ByteOrder: null, To: AstConstantExpression toConstantExpression } && - (toConstantExpression.Value as BsonString)?.Value is { } toValue) - { - var unaryOperator = toValue switch - { - "bool" => AstUnaryOperator.ToBool, - "date" => AstUnaryOperator.ToDate, - "decimal" => AstUnaryOperator.ToDecimal, - "double" => AstUnaryOperator.ToDouble, - "int" => AstUnaryOperator.ToInt, - "long" => AstUnaryOperator.ToLong, - "objectId" => AstUnaryOperator.ToObjectId, - "string" => AstUnaryOperator.ToString, - _ => (AstUnaryOperator?)null - }; - - if (unaryOperator.HasValue) - { - return AstExpression.Unary(unaryOperator.Value, optimizedNode.Input); - } - } - - return optimizedNode; - } - public override AstNode VisitExprFilter(AstExprFilter node) { var optimizedNode = base.VisitExprFilter(node); From 53b886ae1ae408ec4857189bc355d6631fd06e93 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Thu, 3 Apr 2025 12:51:37 +0200 Subject: [PATCH 63/82] Various fixes --- ...essionToAggregationExpressionTranslator.cs | 3 +- ...MethodToAggregationExpressionTranslator.cs | 166 ++++++++++-------- src/MongoDB.Driver/Mql.cs | 23 ++- 3 files changed, 107 insertions(+), 85 deletions(-) diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MemberExpressionToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MemberExpressionToAggregationExpressionTranslator.cs index 58cf7f6ab61..3ff7d0c6b5a 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MemberExpressionToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MemberExpressionToAggregationExpressionTranslator.cs @@ -43,7 +43,6 @@ public static TranslatedExpression Translate(TranslationContext context, MemberE { case "HasValue": return HasValuePropertyToAggregationExpressionTranslator.Translate(context, expression); case "Value": return ValuePropertyToAggregationExpressionTranslator.Translate(context, expression); - default: break; } } @@ -66,7 +65,7 @@ public static TranslatedExpression Translate(TranslationContext context, MemberE if (!DocumentSerializerHelper.AreMembersRepresentedAsFields(containerTranslation.Serializer, out _)) { - if (member is PropertyInfo propertyInfo && propertyInfo.Name == "Length") + if (member is PropertyInfo propertyInfo && propertyInfo.Name == "Length") { return LengthPropertyToAggregationExpressionTranslator.Translate(context, expression); } diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs index fba6049a88e..4b15edb84a2 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs @@ -36,98 +36,108 @@ public static TranslatedExpression Translate(TranslationContext context, MethodC throw new ExpressionNotSupportedException(expression); } - ByteOrder? byteOrder = null; - string format = null; - AstExpression onErrorAst = null; - AstExpression onNullAst = null; - BsonBinarySubType? subType = null; - var fieldAst = ExpressionToAggregationExpressionTranslator.Translate(context, arguments[0]).Ast; - var optionExpression = arguments[1]; + ByteOrder? byteOrder; + string format; + AstExpression onErrorAst; + AstExpression onNullAst; + BsonBinarySubType? subType; - if (optionExpression is ConstantExpression constantExpression) + var optionExpression = arguments[1]; + switch (optionExpression) { - var options = (ConvertOptions)constantExpression.Value; + case ConstantExpression constantExpression: + ExtractOptionsFromConstantExpression(constantExpression, out byteOrder, out format, out onErrorAst, out onNullAst, out subType); + break; + case MemberInitExpression memberInitExpression: + ExtractOptionsFromMemberInitExpression(memberInitExpression, context, out byteOrder, out format, out onErrorAst, out onNullAst, out subType); + break; + default: + throw new ExpressionNotSupportedException("The 'Options' argument can be either a constant expression or a member initialization expression"); + } - if (options.OnErrorWasSet) - { - onErrorAst = options.GetOnError(); - } + var toType = method.GetGenericArguments()[1]; + var toBsonType = GetBsonType(toType).Render(); + var serializer = BsonSerializer.LookupSerializer(toType); - if (options.OnNullWasSet) - { - onNullAst = options.GetOnNull(); - } + var ast = AstExpression.Convert(fieldAst, toBsonType, subType: subType, byteOrder: byteOrder, format: format, onError: onErrorAst, onNull: onNullAst); + return new TranslatedExpression(expression, ast, serializer); + } + + private static void ExtractOptionsFromConstantExpression(ConstantExpression constantExpression, out ByteOrder? byteOrder, out string format, out AstExpression onErrorAst, out AstExpression onNullAst, out BsonBinarySubType? subType) + { + byteOrder = null; + format = null; + onErrorAst = null; + onNullAst = null; + subType = null; - subType = options.SubType; - format = options.Format; - byteOrder = options.ByteOrder; + var options = (ConvertOptions)constantExpression.Value; + + if (options is null) + { + return; } - else if (optionExpression is MemberInitExpression memberInitExpression) + + if (options.OnErrorWasSet) { - foreach (var binding in memberInitExpression.Bindings) - { - if (binding is not MemberAssignment memberAssignment) continue; - - var memberName = memberAssignment.Member.Name; - - switch (memberName) - { - case nameof(ConvertOptions.ByteOrder): - { - if (memberAssignment.Expression is not ConstantExpression byteOrderExpression) - { - throw new ExpressionNotSupportedException($"The {nameof(ConvertOptions.ByteOrder)} field must be a constant expression"); //TODO Improve message? - } - - byteOrder = (ByteOrder?)byteOrderExpression.Value; - break; - } - case nameof(ConvertOptions.Format): - { - if (memberAssignment.Expression is not ConstantExpression formatExpression) - { - throw new ExpressionNotSupportedException($"The {nameof(ConvertOptions.Format)} field must be a constant expression"); //TODO Improve message? - } - - format = (string)formatExpression.Value; - break; - } - case nameof(ConvertOptions.OnError): - { - onErrorAst = ExpressionToAggregationExpressionTranslator.Translate(context, memberAssignment.Expression).Ast; - break; - } - case nameof(ConvertOptions.OnNull): - { - onNullAst = ExpressionToAggregationExpressionTranslator.Translate(context, memberAssignment.Expression).Ast; - break; - } - case nameof(ConvertOptions.SubType): - { - if (memberAssignment.Expression is not ConstantExpression subTypeExpression) - { - throw new ExpressionNotSupportedException($"The {nameof(ConvertOptions.SubType)} field must be a constant expression"); //TODO Improve message? - } - - subType = (BsonBinarySubType?)subTypeExpression.Value; - break; - } - } - } + onErrorAst = options.GetOnError(); } - else + + if (options.OnNullWasSet) { - throw new ExpressionNotSupportedException("The 'Options' argument can be either a constant expression or a member initialization expression"); //TODO Improve message? + onNullAst = options.GetOnNull(); } - var toType = method.GetGenericArguments()[1]; - var toBsonType = GetBsonType(toType).Render(); - var serializer = BsonSerializer.LookupSerializer(toType); + subType = options.SubType; + format = options.Format; + byteOrder = options.ByteOrder; + } - var ast = AstExpression.Convert(fieldAst, toBsonType, subType: subType, byteOrder: byteOrder, format: format, onError: onErrorAst, onNull: onNullAst); - return new TranslatedExpression(expression, ast, serializer); + private static void ExtractOptionsFromMemberInitExpression(MemberInitExpression memberInitExpression, TranslationContext context, out ByteOrder? byteOrder, out string format, out AstExpression onErrorAst, out AstExpression onNullAst, out BsonBinarySubType? subType) + { + byteOrder = null; + format = null; + onErrorAst = null; + onNullAst = null; + subType = null; + + foreach (var binding in memberInitExpression.Bindings) + { + if (binding is not MemberAssignment memberAssignment) continue; + + var memberName = memberAssignment.Member.Name; + var expression = memberAssignment.Expression; + + switch (memberName) + { + case nameof(ConvertOptions.ByteOrder): + byteOrder = GetConstantValue(expression, nameof(ConvertOptions.ByteOrder)); + break; + case nameof(ConvertOptions.Format): + format = GetConstantValue(expression, nameof(ConvertOptions.Format)); + break; + case nameof(ConvertOptions.OnError): + onErrorAst = ExpressionToAggregationExpressionTranslator.Translate(context, expression).Ast; + break; + case nameof(ConvertOptions.OnNull): + onNullAst = ExpressionToAggregationExpressionTranslator.Translate(context, expression).Ast; + break; + case nameof(ConvertOptions.SubType): + subType = GetConstantValue(expression, nameof(ConvertOptions.SubType)); + break; + } + } + } + + private static T GetConstantValue(Expression expression, string fieldName) + { + if (expression is not ConstantExpression constantExpression) + { + throw new ExpressionNotSupportedException($"The {fieldName} field must be a constant expression."); + } + return (T)constantExpression.Value; } private static BsonType GetBsonType(Type type) //TODO Do we have this kind of info somewhere else...? diff --git a/src/MongoDB.Driver/Mql.cs b/src/MongoDB.Driver/Mql.cs index 98b0c839cca..d635ee686ca 100644 --- a/src/MongoDB.Driver/Mql.cs +++ b/src/MongoDB.Driver/Mql.cs @@ -17,6 +17,7 @@ using MongoDB.Driver.Linq.Linq3Implementation.Misc; using MongoDB.Bson; using MongoDB.Bson.Serialization; +using MongoDB.Driver.Linq.Linq3Implementation.Serializers; namespace MongoDB.Driver { @@ -51,7 +52,7 @@ public static TValue Constant(TValue value, BsonType representaion) /// - /// Converts a value from one type to another using the specified options. + /// Converts a value from one type to another using the $convert aggregation operator. /// /// The type of the input value. /// The type of the output value. @@ -185,7 +186,7 @@ public enum ByteOrder } /// - /// Represents the options parameter for the conversion methods in the Mql static class. + /// Represents the typed options parameter for . /// public abstract class ConvertOptions { @@ -229,8 +230,9 @@ public BsonBinarySubType? SubType internal abstract BsonValue GetOnNull(); } + /// - /// Represents the typed options parameter for the conversion methods in the Mql static class. + /// Represents the typed options parameter for . /// This class allows to set 'onError' and 'onNull'. /// /// The type of 'onError' and 'onNull'. @@ -240,6 +242,17 @@ public class ConvertOptions : ConvertOptions private bool _onErrorWasSet; private TTo _onNull; private bool _onNullWasSet; + private readonly IBsonSerializer _serializer; + + /// + /// Initializes a new instance of the class. + /// + public ConvertOptions() + { + _serializer = StandardSerializers.TryGetSerializer(typeof(TTo), out var serializer) + ? serializer + : BsonSerializer.LookupSerializer(typeof(TTo)); + } /// /// The onError parameter. @@ -270,8 +283,8 @@ public TTo OnNull internal override bool OnErrorWasSet => _onErrorWasSet; internal override bool OnNullWasSet => _onNullWasSet; - internal override BsonValue GetOnError() => BsonValue.Create(_onError); + internal override BsonValue GetOnError() => _serializer.ToBsonValue(_onError); - internal override BsonValue GetOnNull() => BsonValue.Create(_onNull); + internal override BsonValue GetOnNull() => _serializer.ToBsonValue(_onNull); } } From 37cb3735b0e4046df3dcea22882bb0be02f5a9f0 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Thu, 3 Apr 2025 15:07:47 +0200 Subject: [PATCH 64/82] added more tests --- ...dToAggregationExpressionTranslatorTests.cs | 260 +++++++++++++++--- 1 file changed, 229 insertions(+), 31 deletions(-) diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs index 5566c4685c8..99a14ac8bf9 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs @@ -32,13 +32,195 @@ public ConvertMethodToAggregationExpressionTranslatorTests(ClassFixture fixture) { } + [Theory] + [InlineData(BsonBinarySubType.Binary, 0)] + [InlineData(BsonBinarySubType.Sensitive, 8)] + [InlineData(BsonBinarySubType.UserDefined, 128)] + public void Convert_with_BsonBinaryData_and_subtype_should_be_rendered_correctly(BsonBinarySubType subType, int expectedSubTypeValue) + { + RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromString); + + const int id = 3; + var collection = Fixture.Collection; + var queryable = collection.AsQueryable() + .Where(x => x.Id == id) + .Select(x => Mql.Convert(x.DoubleProperty, new ConvertOptions { ByteOrder = ByteOrder.LittleEndian, SubType = subType })); + + var expectedStages = + new[] + { + $"{{ $match : {{ _id : {id} }} }}", + $"{{ $project: {{ _v : {{ $convert : {{ input : '$DoubleProperty', to : {{ type: 'binData', subtype: {expectedSubTypeValue} }}, 'byteOrder': 'little' }} }}, _id : 0 }} }}", + }; + + var expectedResult = new BsonBinaryData(Convert.FromBase64String("AAAAAAAA4L8="), subType); + AssertOutcome(collection, queryable, expectedStages, expectedResult); + } + + [Theory] + [InlineData(3, ByteOrder.LittleEndian,"AAAAAAAA4L8=")] + [InlineData(5, ByteOrder.BigEndian, "wAQAAAAAAAA=" )] + public void Convert_with_BsonBinaryData_and_byteOrder_should_be_rendered_correctly(int id, ByteOrder byteOrder, string expectedBase64) + { + RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromString); + + var collection = Fixture.Collection; + var queryable = collection.AsQueryable() + .Where(x => x.Id == id) + .Select(x => Mql.Convert(x.DoubleProperty, new ConvertOptions { ByteOrder = byteOrder, SubType = BsonBinarySubType.Binary })); + + var expectedStages = + new[] + { + $"{{ $match : {{ _id : {id} }} }}", + $"{{ $project: {{ _v : {{ $convert : {{ input : '$DoubleProperty', to : {{ type: 'binData', subtype: 0 }}, {ByteOrderToString(byteOrder)} }} }}, _id : 0 }} }}", + }; + + var expectedResult = new BsonBinaryData(Convert.FromBase64String(expectedBase64)); + AssertOutcome(collection, queryable, expectedStages, expectedResult); + } + + [Theory] + [InlineData("uuid", "867dee52-c331-484e-92d1-c56479b8e67e")] + [InlineData("base64", "hn3uUsMxSE6S0cVkebjmfg==")] + public void Convert_with_BsonBinaryData_and_format_should_be_rendered_correctly(string format, string expectedResult) + { + RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromString); + + const int id = 2; + var collection = Fixture.Collection; + var queryable = collection.AsQueryable() + .Where(x => x.Id == id) + .Select(x => Mql.Convert(x.BinaryProperty, new ConvertOptions { Format = format })); + + var expectedStages = + new[] + { + $"{{ $match : {{ _id : {id} }} }}", + $"{{ $project: {{ _v : {{ $convert : {{ input : '$BinaryProperty', to : 'string', format: '{format}' }} }}, _id : 0 }} }}", + }; + + AssertOutcome(collection, queryable, expectedStages, expectedResult); + } + + [Theory] + [InlineData(null)] + [InlineData(25)] + public void Convert_with_constant_OnError_should_work(int? onError) + { + RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromString); + + const int id = 20; + var collection = Fixture.Collection; + var queryable = collection.AsQueryable() + .Where(x => x.Id == id) + .Select(x => Mql.Convert(x.StringProperty, new ConvertOptions { OnError = onError })); + + var onErrorVal = onError == null ? "null" : onError.ToString(); + var expectedStages = + new[] + { + $"{{ $match : {{ _id : {id} }} }}", + $"{{ $project: {{ _v : {{ $convert : {{ input : '$StringProperty', to : 'int', onError: {onErrorVal} }} }}, _id : 0 }} }}", + }; + + AssertOutcome(collection, queryable, expectedStages, onError); + } + + [Fact] + public void Convert_with_field_OnError_should_work() + { + RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromString); + + const int id = 20; + var collection = Fixture.Collection; + var queryable = collection.AsQueryable() + .Where(x => x.Id == id) + .Select(x => Mql.Convert(x.StringProperty, new ConvertOptions { OnError = x.IntProperty })); + + var expectedStages = + new[] + { + $"{{ $match : {{ _id : {id} }} }}", + $"{{ $project: {{ _v : {{ $convert : {{ input : '$StringProperty', to : 'int', onError: '$IntProperty' }} }}, _id : 0 }} }}", + }; + + AssertOutcome(collection, queryable, expectedStages, 22); + } + + [Theory] + [InlineData(null)] + [InlineData(25)] + public void Convert_with_constant_OnNull_should_work(int? onNull) + { + RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromString); + + const int id = 0; + var collection = Fixture.Collection; + var queryable = collection.AsQueryable() + .Where(x => x.Id == id) + .Select(x => Mql.Convert(x.StringProperty, new ConvertOptions { OnNull = onNull })); + + var onNullVal = onNull == null ? "null" : onNull.ToString(); + var expectedStages = + new[] + { + $"{{ $match : {{ _id : {id} }} }}", + $"{{ $project: {{ _v : {{ $convert : {{ input : '$StringProperty', to : 'int', onNull: {onNullVal} }} }}, _id : 0 }} }}", + }; + + AssertOutcome(collection, queryable, expectedStages, onNull); + } + + [Fact] + public void Convert_with_field_OnNull_should_work() + { + RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromString); + + const int id = 22; + var collection = Fixture.Collection; + var queryable = collection.AsQueryable() + .Where(x => x.Id == id) + .Select(x => Mql.Convert(x.StringProperty, new ConvertOptions { OnNull = x.IntProperty })); + + var expectedStages = + new[] + { + $"{{ $match : {{ _id : {id} }} }}", + $"{{ $project: {{ _v : {{ $convert : {{ input : '$StringProperty', to : 'int', onNull: '$IntProperty' }} }}, _id : 0 }} }}", + }; + + AssertOutcome(collection, queryable, expectedStages, 33); + } + + [Fact] + public void Convert_without_options_should_be_reduced_if_possible() + { + RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromString); + + const int id = 0; + var collection = Fixture.Collection; + var queryable = collection.AsQueryable() + .Where(x => x.Id == id) + .Select(x => Mql.Convert(x.StringProperty, null)); + + var expectedStages = + new[] + { + $"{{ $match : {{ _id : {id} }} }}", + $"{{ $project: {{ _v : {{ $toInt : '$StringProperty' }}, _id : 0 }} }}", + }; + + AssertOutcome(collection, queryable, expectedStages, 15); + } + [Theory] [InlineData(3, ByteOrder.LittleEndian,"AAAAAAAA4L8=", null)] [InlineData(5, ByteOrder.BigEndian, "wAQAAAAAAAA=", null )] [InlineData(10, ByteOrder.BigEndian, null, "MongoCommandException")] - public void Test1(int id, ByteOrder byteOrder, string expectedBase64, string expectedException) + public void Convert_to_BsonBinaryData_from_double_should_work(int id, ByteOrder byteOrder, string expectedBase64, string expectedException) { - RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); + RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromString); var collection = Fixture.Collection; var queryable = collection.AsQueryable() @@ -57,64 +239,64 @@ public void Test1(int id, ByteOrder byteOrder, string expectedBase64, string exp } [Theory] - [InlineData(3, ByteOrder.LittleEndian,"AAAAAAAA4L8=", null)] - [InlineData(5, ByteOrder.BigEndian, "wAQAAAAAAAA=", null )] - [InlineData(10, ByteOrder.BigEndian, null, "MongoCommandException")] - public void Test2(int id, ByteOrder byteOrder, string expectedBase64, string expectedException) + [InlineData(2, ByteOrder.BigEndian, 0, "MongoCommandException")] + [InlineData(3, ByteOrder.LittleEndian, -0.5, null)] + [InlineData(5, ByteOrder.BigEndian, -2.5, null)] + public void Convert_to_double_from_BsonBinaryData_should_work(int id, ByteOrder byteOrder, double expectedResult, string expectedException) { RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); var collection = Fixture.Collection; var queryable = collection.AsQueryable() .Where(x => x.Id == id) - .Select(x => Mql.Convert(x.DoubleProperty, new ConvertOptions { ByteOrder = byteOrder, SubType = BsonBinarySubType.Binary })); + .Select(x => Mql.Convert(x.BinaryProperty, new ConvertOptions { ByteOrder = byteOrder })); var expectedStages = new[] { $"{{ $match : {{ _id : {id} }} }}", - $"{{ $project: {{ _v : {{ $convert : {{ input : '$DoubleProperty', to : {{ type: 'binData', subtype: 0 }}, {ByteOrderToString(byteOrder)} }} }}, _id : 0 }} }}", + $"{{ $project: {{ _v : {{ $convert : {{ input : '$BinaryProperty', to : 'double', {ByteOrderToString(byteOrder)} }} }}, _id : 0 }} }}", }; - var expectedResult = expectedBase64 == null ? default : Convert.FromBase64String(expectedBase64); AssertOutcome(collection, queryable, expectedStages, expectedResult, expectedException); } [Theory] - [InlineData(0, ByteOrder.BigEndian, null, null)] - [InlineData(2, ByteOrder.LittleEndian, null, "MongoCommandException")] - [InlineData(4, ByteOrder.LittleEndian, 674, null)] - [InlineData(6, ByteOrder.BigEndian, 42, null)] - public void Test3(int id, ByteOrder byteOrder, int? expectedResult, string expectedException) + [InlineData(4, ByteOrder.LittleEndian,"ogIAAA==", null)] + [InlineData(6, ByteOrder.BigEndian, "AAAAKg==", null )] + [InlineData(10, ByteOrder.BigEndian, null, "MongoCommandException")] + public void Convert_to_BsonBinaryData_from_int_should_work(int id, ByteOrder byteOrder, string expectedBase64, string expectedException) { - RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); + RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromString); var collection = Fixture.Collection; var queryable = collection.AsQueryable() .Where(x => x.Id == id) - .Select(x => Mql.Convert(x.BinaryProperty, new ConvertOptions {ByteOrder = byteOrder})); + .Select(x => Mql.Convert(x.IntProperty, new ConvertOptions { ByteOrder = byteOrder, SubType = BsonBinarySubType.Binary })); var expectedStages = new[] { $"{{ $match : {{ _id : {id} }} }}", - $"{{ $project: {{ _v : {{ $convert : {{ input : '$BinaryProperty', to : 'int', {ByteOrderToString(byteOrder)} }} }}, _id : 0 }} }}", + $"{{ $project: {{ _v : {{ $convert : {{ input : '$IntProperty', to : {{ type: 'binData', subtype: 0 }}, {ByteOrderToString(byteOrder)} }} }}, _id : 0 }} }}", }; + var expectedResult = expectedBase64 == null ? default : new BsonBinaryData(Convert.FromBase64String(expectedBase64)); AssertOutcome(collection, queryable, expectedStages, expectedResult, expectedException); } [Theory] + [InlineData(2, ByteOrder.LittleEndian, 0, "MongoCommandException")] [InlineData(4, ByteOrder.LittleEndian, 674, null)] [InlineData(6, ByteOrder.BigEndian, 42, null)] - public void Test4(int id, ByteOrder byteOrder, int expectedResult, string expectedException) + public void Convert_to_int_from_BsonBinaryData_should_work(int id, ByteOrder byteOrder, int expectedResult, string expectedException) { RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); var collection = Fixture.Collection; var queryable = collection.AsQueryable() .Where(x => x.Id == id) - .Select(x => Mql.Convert(x.BinaryProperty, new ConvertOptions {ByteOrder = byteOrder})); + .Select(x => Mql.Convert(x.BinaryProperty, new ConvertOptions { ByteOrder = byteOrder })); var expectedStages = new[] @@ -127,42 +309,56 @@ public void Test4(int id, ByteOrder byteOrder, int expectedResult, string expect } [Theory] - [InlineData(0, ByteOrder.BigEndian, 50, null)] - public void Test5(int id, ByteOrder byteOrder, int? expectedResult, string expectedException) + [InlineData(4, ByteOrder.LittleEndian,"ogIAAA==", null)] + [InlineData(6, ByteOrder.BigEndian, "AAAAKg==", null )] + [InlineData(10, ByteOrder.BigEndian, null, "MongoCommandException")] + public void Convert_to_BsonBinaryData_from_long_should_work(int id, ByteOrder byteOrder, string expectedBase64, string expectedException) { - RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); + RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromString); var collection = Fixture.Collection; var queryable = collection.AsQueryable() .Where(x => x.Id == id) - .Select(x => Mql.Convert(x.BinaryProperty, new ConvertOptions {ByteOrder = byteOrder, OnNull = 50})); + .Select(x => Mql.Convert(x.LongProperty, new ConvertOptions { ByteOrder = byteOrder, SubType = BsonBinarySubType.Binary })); var expectedStages = new[] { $"{{ $match : {{ _id : {id} }} }}", - $"{{ $project: {{ _v : {{ $convert : {{ input : '$BinaryProperty', to : 'int', 'onNull': 50, {ByteOrderToString(byteOrder)} }} }}, _id : 0 }} }}", + $"{{ $project: {{ _v : {{ $convert : {{ input : '$LongProperty', to : {{ type: 'binData', subtype: 0 }}, {ByteOrderToString(byteOrder)} }} }}, _id : 0 }} }}", }; + BsonBinaryData expectedResult = null; + + if (expectedBase64 is not null) + { + //$convert to BinData returns always 8 bytes when from long + var expectedBytes = new byte[8]; + Array.Copy(Convert.FromBase64String(expectedBase64), 0, expectedBytes, byteOrder is ByteOrder.LittleEndian ? 0 : 4, 4); + expectedResult = new BsonBinaryData(expectedBytes); + } + AssertOutcome(collection, queryable, expectedStages, expectedResult, expectedException); } [Theory] - [InlineData(0, ByteOrder.BigEndian, 22, null)] - public void Test6(int id, ByteOrder byteOrder, int? expectedResult, string expectedException) + [InlineData(2, ByteOrder.LittleEndian, 0, "MongoCommandException")] + [InlineData(4, ByteOrder.LittleEndian, (long)674, null)] + [InlineData(6, ByteOrder.BigEndian, (long)42, null)] + public void Convert_to_long_from_BsonBinaryData_should_work(int id, ByteOrder byteOrder, long expectedResult, string expectedException) { RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); var collection = Fixture.Collection; var queryable = collection.AsQueryable() .Where(x => x.Id == id) - .Select(x => Mql.Convert(x.BinaryProperty, new ConvertOptions {ByteOrder = byteOrder, OnNull = x.ExtraIntProperty})); + .Select(x => Mql.Convert(x.BinaryProperty, new ConvertOptions { ByteOrder = byteOrder })); var expectedStages = new[] { $"{{ $match : {{ _id : {id} }} }}", - $"{{ $project: {{ _v : {{ $convert : {{ input : '$BinaryProperty', to : 'int', 'onNull': '$ExtraIntProperty', {ByteOrderToString(byteOrder)} }} }}, _id : 0 }} }}", + $"{{ $project: {{ _v : {{ $convert : {{ input : '$BinaryProperty', to : 'long', {ByteOrderToString(byteOrder)} }} }}, _id : 0 }} }}", }; AssertOutcome(collection, queryable, expectedStages, expectedResult, expectedException); @@ -208,7 +404,7 @@ public sealed class ClassFixture : MongoCollectionFixture InitialData => [ - BsonDocument.Parse("{ _id : 0, ExtraIntProperty : 22 }"), + BsonDocument.Parse("{ _id : 0 }"), BsonDocument.Parse("{ _id : 1, BinaryProperty : BinData(0, 'ogIAAA==') }"), BsonDocument.Parse("{ _id : 2, BinaryProperty : BinData(4, 'hn3uUsMxSE6S0cVkebjmfg=='), StringProperty: '867dee52-c331-484e-92d1-c56479b8e67e' }"), BsonDocument.Parse("{ _id : 3, BinaryProperty : BinData(0, 'AAAAAAAA4L8='), DoubleProperty: -0.5, NullableDoubleProperty: -0.5 }"), // LittleEndian @@ -216,7 +412,10 @@ public sealed class ClassFixture : MongoCollectionFixture Date: Thu, 3 Apr 2025 15:08:29 +0200 Subject: [PATCH 65/82] Small rename --- .../ConvertMethodToAggregationExpressionTranslatorTests.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs index 99a14ac8bf9..8c558756da7 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs @@ -36,7 +36,7 @@ public ConvertMethodToAggregationExpressionTranslatorTests(ClassFixture fixture) [InlineData(BsonBinarySubType.Binary, 0)] [InlineData(BsonBinarySubType.Sensitive, 8)] [InlineData(BsonBinarySubType.UserDefined, 128)] - public void Convert_with_BsonBinaryData_and_subtype_should_be_rendered_correctly(BsonBinarySubType subType, int expectedSubTypeValue) + public void Convert_with_subtype_should_be_rendered_correctly(BsonBinarySubType subType, int expectedSubTypeValue) { RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromString); @@ -60,7 +60,7 @@ public void Convert_with_BsonBinaryData_and_subtype_should_be_rendered_correctly [Theory] [InlineData(3, ByteOrder.LittleEndian,"AAAAAAAA4L8=")] [InlineData(5, ByteOrder.BigEndian, "wAQAAAAAAAA=" )] - public void Convert_with_BsonBinaryData_and_byteOrder_should_be_rendered_correctly(int id, ByteOrder byteOrder, string expectedBase64) + public void Convert_with_byteOrder_should_be_rendered_correctly(int id, ByteOrder byteOrder, string expectedBase64) { RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromString); @@ -83,7 +83,7 @@ public void Convert_with_BsonBinaryData_and_byteOrder_should_be_rendered_correct [Theory] [InlineData("uuid", "867dee52-c331-484e-92d1-c56479b8e67e")] [InlineData("base64", "hn3uUsMxSE6S0cVkebjmfg==")] - public void Convert_with_BsonBinaryData_and_format_should_be_rendered_correctly(string format, string expectedResult) + public void Convert_with_format_should_be_rendered_correctly(string format, string expectedResult) { RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromString); From 5fa585080ff8c4434ed6cd220d19b89c5775bd38 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Thu, 3 Apr 2025 15:42:02 +0200 Subject: [PATCH 66/82] Reordered tests --- ...dToAggregationExpressionTranslatorTests.cs | 253 ++++++++++-------- 1 file changed, 137 insertions(+), 116 deletions(-) diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs index 8c558756da7..f03430614c3 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs @@ -33,34 +33,67 @@ public ConvertMethodToAggregationExpressionTranslatorTests(ClassFixture fixture) } [Theory] - [InlineData(BsonBinarySubType.Binary, 0)] - [InlineData(BsonBinarySubType.Sensitive, 8)] - [InlineData(BsonBinarySubType.UserDefined, 128)] - public void Convert_with_subtype_should_be_rendered_correctly(BsonBinarySubType subType, int expectedSubTypeValue) + [InlineData(4, ByteOrder.LittleEndian,"ogIAAA==", null)] + [InlineData(6, ByteOrder.BigEndian, "AAAAKg==", null )] + [InlineData(10, ByteOrder.BigEndian, null, "MongoCommandException")] + public void Convert_to_BsonBinaryData_from_int_should_work(int id, ByteOrder byteOrder, string expectedBase64, string expectedException) { RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromString); - const int id = 3; var collection = Fixture.Collection; var queryable = collection.AsQueryable() .Where(x => x.Id == id) - .Select(x => Mql.Convert(x.DoubleProperty, new ConvertOptions { ByteOrder = ByteOrder.LittleEndian, SubType = subType })); + .Select(x => Mql.Convert(x.IntProperty, new ConvertOptions { ByteOrder = byteOrder, SubType = BsonBinarySubType.Binary })); var expectedStages = new[] { $"{{ $match : {{ _id : {id} }} }}", - $"{{ $project: {{ _v : {{ $convert : {{ input : '$DoubleProperty', to : {{ type: 'binData', subtype: {expectedSubTypeValue} }}, 'byteOrder': 'little' }} }}, _id : 0 }} }}", + $"{{ $project: {{ _v : {{ $convert : {{ input : '$IntProperty', to : {{ type: 'binData', subtype: 0 }}, {ByteOrderToString(byteOrder)} }} }}, _id : 0 }} }}", }; - var expectedResult = new BsonBinaryData(Convert.FromBase64String("AAAAAAAA4L8="), subType); - AssertOutcome(collection, queryable, expectedStages, expectedResult); + var expectedResult = expectedBase64 == null ? default : new BsonBinaryData(Convert.FromBase64String(expectedBase64)); + AssertOutcome(collection, queryable, expectedStages, expectedResult, expectedException); } [Theory] - [InlineData(3, ByteOrder.LittleEndian,"AAAAAAAA4L8=")] - [InlineData(5, ByteOrder.BigEndian, "wAQAAAAAAAA=" )] - public void Convert_with_byteOrder_should_be_rendered_correctly(int id, ByteOrder byteOrder, string expectedBase64) + [InlineData(4, ByteOrder.LittleEndian,"ogIAAA==", null)] + [InlineData(6, ByteOrder.BigEndian, "AAAAKg==", null )] + [InlineData(10, ByteOrder.BigEndian, null, "MongoCommandException")] + public void Convert_to_BsonBinaryData_from_long_should_work(int id, ByteOrder byteOrder, string expectedBase64, string expectedException) + { + RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromString); + + var collection = Fixture.Collection; + var queryable = collection.AsQueryable() + .Where(x => x.Id == id) + .Select(x => Mql.Convert(x.LongProperty, new ConvertOptions { ByteOrder = byteOrder, SubType = BsonBinarySubType.Binary })); + + var expectedStages = + new[] + { + $"{{ $match : {{ _id : {id} }} }}", + $"{{ $project: {{ _v : {{ $convert : {{ input : '$LongProperty', to : {{ type: 'binData', subtype: 0 }}, {ByteOrderToString(byteOrder)} }} }}, _id : 0 }} }}", + }; + + BsonBinaryData expectedResult = null; + + if (expectedBase64 is not null) + { + //$convert to BinData returns always 8 bytes when from long + var expectedBytes = new byte[8]; + Array.Copy(Convert.FromBase64String(expectedBase64), 0, expectedBytes, byteOrder is ByteOrder.LittleEndian ? 0 : 4, 4); + expectedResult = new BsonBinaryData(expectedBytes); + } + + AssertOutcome(collection, queryable, expectedStages, expectedResult, expectedException); + } + + [Theory] + [InlineData(3, ByteOrder.LittleEndian,"AAAAAAAA4L8=", null)] + [InlineData(5, ByteOrder.BigEndian, "wAQAAAAAAAA=", null )] + [InlineData(10, ByteOrder.BigEndian, null, "MongoCommandException")] + public void Convert_to_BsonBinaryData_from_double_should_work(int id, ByteOrder byteOrder, string expectedBase64, string expectedException) { RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromString); @@ -76,292 +109,280 @@ public void Convert_with_byteOrder_should_be_rendered_correctly(int id, ByteOrde $"{{ $project: {{ _v : {{ $convert : {{ input : '$DoubleProperty', to : {{ type: 'binData', subtype: 0 }}, {ByteOrderToString(byteOrder)} }} }}, _id : 0 }} }}", }; - var expectedResult = new BsonBinaryData(Convert.FromBase64String(expectedBase64)); - AssertOutcome(collection, queryable, expectedStages, expectedResult); + var expectedResult = expectedBase64 == null ? default : new BsonBinaryData(Convert.FromBase64String(expectedBase64)); + AssertOutcome(collection, queryable, expectedStages, expectedResult, expectedException); } [Theory] - [InlineData("uuid", "867dee52-c331-484e-92d1-c56479b8e67e")] - [InlineData("base64", "hn3uUsMxSE6S0cVkebjmfg==")] - public void Convert_with_format_should_be_rendered_correctly(string format, string expectedResult) + [InlineData(2, ByteOrder.LittleEndian, 0, "MongoCommandException")] + [InlineData(4, ByteOrder.LittleEndian, 674, null)] + [InlineData(6, ByteOrder.BigEndian, 42, null)] + public void Convert_to_int_from_BsonBinaryData_should_work(int id, ByteOrder byteOrder, int expectedResult, string expectedException) { - RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromString); + RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); - const int id = 2; var collection = Fixture.Collection; var queryable = collection.AsQueryable() .Where(x => x.Id == id) - .Select(x => Mql.Convert(x.BinaryProperty, new ConvertOptions { Format = format })); + .Select(x => Mql.Convert(x.BinaryProperty, new ConvertOptions { ByteOrder = byteOrder })); var expectedStages = new[] { $"{{ $match : {{ _id : {id} }} }}", - $"{{ $project: {{ _v : {{ $convert : {{ input : '$BinaryProperty', to : 'string', format: '{format}' }} }}, _id : 0 }} }}", + $"{{ $project: {{ _v : {{ $convert : {{ input : '$BinaryProperty', to : 'int', {ByteOrderToString(byteOrder)} }} }}, _id : 0 }} }}", }; - AssertOutcome(collection, queryable, expectedStages, expectedResult); + AssertOutcome(collection, queryable, expectedStages, expectedResult, expectedException); } [Theory] - [InlineData(null)] - [InlineData(25)] - public void Convert_with_constant_OnError_should_work(int? onError) + [InlineData(2, ByteOrder.LittleEndian, 0, "MongoCommandException")] + [InlineData(4, ByteOrder.LittleEndian, (long)674, null)] + [InlineData(6, ByteOrder.BigEndian, (long)42, null)] + public void Convert_to_long_from_BsonBinaryData_should_work(int id, ByteOrder byteOrder, long expectedResult, string expectedException) { - RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromString); + RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); - const int id = 20; var collection = Fixture.Collection; var queryable = collection.AsQueryable() .Where(x => x.Id == id) - .Select(x => Mql.Convert(x.StringProperty, new ConvertOptions { OnError = onError })); + .Select(x => Mql.Convert(x.BinaryProperty, new ConvertOptions { ByteOrder = byteOrder })); - var onErrorVal = onError == null ? "null" : onError.ToString(); var expectedStages = new[] { $"{{ $match : {{ _id : {id} }} }}", - $"{{ $project: {{ _v : {{ $convert : {{ input : '$StringProperty', to : 'int', onError: {onErrorVal} }} }}, _id : 0 }} }}", + $"{{ $project: {{ _v : {{ $convert : {{ input : '$BinaryProperty', to : 'long', {ByteOrderToString(byteOrder)} }} }}, _id : 0 }} }}", }; - AssertOutcome(collection, queryable, expectedStages, onError); + AssertOutcome(collection, queryable, expectedStages, expectedResult, expectedException); } - [Fact] - public void Convert_with_field_OnError_should_work() + [Theory] + [InlineData(2, ByteOrder.BigEndian, 0, "MongoCommandException")] + [InlineData(3, ByteOrder.LittleEndian, -0.5, null)] + [InlineData(5, ByteOrder.BigEndian, -2.5, null)] + public void Convert_to_double_from_BsonBinaryData_should_work(int id, ByteOrder byteOrder, double expectedResult, string expectedException) { - RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromString); + RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); - const int id = 20; var collection = Fixture.Collection; var queryable = collection.AsQueryable() .Where(x => x.Id == id) - .Select(x => Mql.Convert(x.StringProperty, new ConvertOptions { OnError = x.IntProperty })); + .Select(x => Mql.Convert(x.BinaryProperty, new ConvertOptions { ByteOrder = byteOrder })); var expectedStages = new[] { $"{{ $match : {{ _id : {id} }} }}", - $"{{ $project: {{ _v : {{ $convert : {{ input : '$StringProperty', to : 'int', onError: '$IntProperty' }} }}, _id : 0 }} }}", + $"{{ $project: {{ _v : {{ $convert : {{ input : '$BinaryProperty', to : 'double', {ByteOrderToString(byteOrder)} }} }}, _id : 0 }} }}", }; - AssertOutcome(collection, queryable, expectedStages, 22); + AssertOutcome(collection, queryable, expectedStages, expectedResult, expectedException); } [Theory] [InlineData(null)] [InlineData(25)] - public void Convert_with_constant_OnNull_should_work(int? onNull) + public void Convert_with_constant_OnError_should_work(int? onError) { RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromString); - const int id = 0; + const int id = 20; var collection = Fixture.Collection; var queryable = collection.AsQueryable() .Where(x => x.Id == id) - .Select(x => Mql.Convert(x.StringProperty, new ConvertOptions { OnNull = onNull })); + .Select(x => Mql.Convert(x.StringProperty, new ConvertOptions { OnError = onError })); - var onNullVal = onNull == null ? "null" : onNull.ToString(); + var onErrorVal = onError == null ? "null" : onError.ToString(); var expectedStages = new[] { $"{{ $match : {{ _id : {id} }} }}", - $"{{ $project: {{ _v : {{ $convert : {{ input : '$StringProperty', to : 'int', onNull: {onNullVal} }} }}, _id : 0 }} }}", + $"{{ $project: {{ _v : {{ $convert : {{ input : '$StringProperty', to : 'int', onError: {onErrorVal} }} }}, _id : 0 }} }}", }; - AssertOutcome(collection, queryable, expectedStages, onNull); + AssertOutcome(collection, queryable, expectedStages, onError); } - [Fact] - public void Convert_with_field_OnNull_should_work() + [Theory] + [InlineData(null)] + [InlineData(25)] + public void Convert_with_constant_OnNull_should_work(int? onNull) { RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromString); - const int id = 22; + const int id = 0; var collection = Fixture.Collection; var queryable = collection.AsQueryable() .Where(x => x.Id == id) - .Select(x => Mql.Convert(x.StringProperty, new ConvertOptions { OnNull = x.IntProperty })); + .Select(x => Mql.Convert(x.StringProperty, new ConvertOptions { OnNull = onNull })); + var onNullVal = onNull == null ? "null" : onNull.ToString(); var expectedStages = new[] { $"{{ $match : {{ _id : {id} }} }}", - $"{{ $project: {{ _v : {{ $convert : {{ input : '$StringProperty', to : 'int', onNull: '$IntProperty' }} }}, _id : 0 }} }}", + $"{{ $project: {{ _v : {{ $convert : {{ input : '$StringProperty', to : 'int', onNull: {onNullVal} }} }}, _id : 0 }} }}", }; - AssertOutcome(collection, queryable, expectedStages, 33); + AssertOutcome(collection, queryable, expectedStages, onNull); } [Fact] - public void Convert_without_options_should_be_reduced_if_possible() + public void Convert_with_field_OnError_should_work() { RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromString); - const int id = 0; + const int id = 20; var collection = Fixture.Collection; var queryable = collection.AsQueryable() .Where(x => x.Id == id) - .Select(x => Mql.Convert(x.StringProperty, null)); + .Select(x => Mql.Convert(x.StringProperty, new ConvertOptions { OnError = x.IntProperty })); var expectedStages = new[] { $"{{ $match : {{ _id : {id} }} }}", - $"{{ $project: {{ _v : {{ $toInt : '$StringProperty' }}, _id : 0 }} }}", + $"{{ $project: {{ _v : {{ $convert : {{ input : '$StringProperty', to : 'int', onError: '$IntProperty' }} }}, _id : 0 }} }}", }; - AssertOutcome(collection, queryable, expectedStages, 15); + AssertOutcome(collection, queryable, expectedStages, 22); } - [Theory] - [InlineData(3, ByteOrder.LittleEndian,"AAAAAAAA4L8=", null)] - [InlineData(5, ByteOrder.BigEndian, "wAQAAAAAAAA=", null )] - [InlineData(10, ByteOrder.BigEndian, null, "MongoCommandException")] - public void Convert_to_BsonBinaryData_from_double_should_work(int id, ByteOrder byteOrder, string expectedBase64, string expectedException) + [Fact] + public void Convert_with_field_OnNull_should_work() { RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromString); + const int id = 22; var collection = Fixture.Collection; var queryable = collection.AsQueryable() .Where(x => x.Id == id) - .Select(x => Mql.Convert(x.DoubleProperty, new ConvertOptions { ByteOrder = byteOrder, SubType = BsonBinarySubType.Binary })); + .Select(x => Mql.Convert(x.StringProperty, new ConvertOptions { OnNull = x.IntProperty })); var expectedStages = new[] { $"{{ $match : {{ _id : {id} }} }}", - $"{{ $project: {{ _v : {{ $convert : {{ input : '$DoubleProperty', to : {{ type: 'binData', subtype: 0 }}, {ByteOrderToString(byteOrder)} }} }}, _id : 0 }} }}", + $"{{ $project: {{ _v : {{ $convert : {{ input : '$StringProperty', to : 'int', onNull: '$IntProperty' }} }}, _id : 0 }} }}", }; - var expectedResult = expectedBase64 == null ? default : new BsonBinaryData(Convert.FromBase64String(expectedBase64)); - AssertOutcome(collection, queryable, expectedStages, expectedResult, expectedException); + AssertOutcome(collection, queryable, expectedStages, 33); } [Theory] - [InlineData(2, ByteOrder.BigEndian, 0, "MongoCommandException")] - [InlineData(3, ByteOrder.LittleEndian, -0.5, null)] - [InlineData(5, ByteOrder.BigEndian, -2.5, null)] - public void Convert_to_double_from_BsonBinaryData_should_work(int id, ByteOrder byteOrder, double expectedResult, string expectedException) + [InlineData("uuid", "867dee52-c331-484e-92d1-c56479b8e67e")] + [InlineData("base64", "hn3uUsMxSE6S0cVkebjmfg==")] + public void Convert_with_format_should_be_rendered_correctly(string format, string expectedResult) { - RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); + RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromString); + const int id = 2; var collection = Fixture.Collection; var queryable = collection.AsQueryable() .Where(x => x.Id == id) - .Select(x => Mql.Convert(x.BinaryProperty, new ConvertOptions { ByteOrder = byteOrder })); + .Select(x => Mql.Convert(x.BinaryProperty, new ConvertOptions { Format = format })); var expectedStages = new[] { $"{{ $match : {{ _id : {id} }} }}", - $"{{ $project: {{ _v : {{ $convert : {{ input : '$BinaryProperty', to : 'double', {ByteOrderToString(byteOrder)} }} }}, _id : 0 }} }}", + $"{{ $project: {{ _v : {{ $convert : {{ input : '$BinaryProperty', to : 'string', format: '{format}' }} }}, _id : 0 }} }}", }; - AssertOutcome(collection, queryable, expectedStages, expectedResult, expectedException); + AssertOutcome(collection, queryable, expectedStages, expectedResult); } [Theory] - [InlineData(4, ByteOrder.LittleEndian,"ogIAAA==", null)] - [InlineData(6, ByteOrder.BigEndian, "AAAAKg==", null )] - [InlineData(10, ByteOrder.BigEndian, null, "MongoCommandException")] - public void Convert_to_BsonBinaryData_from_int_should_work(int id, ByteOrder byteOrder, string expectedBase64, string expectedException) + [InlineData(3, ByteOrder.LittleEndian,"AAAAAAAA4L8=")] + [InlineData(5, ByteOrder.BigEndian, "wAQAAAAAAAA=" )] + public void Convert_with_byteOrder_should_be_rendered_correctly(int id, ByteOrder byteOrder, string expectedBase64) { RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromString); var collection = Fixture.Collection; var queryable = collection.AsQueryable() .Where(x => x.Id == id) - .Select(x => Mql.Convert(x.IntProperty, new ConvertOptions { ByteOrder = byteOrder, SubType = BsonBinarySubType.Binary })); + .Select(x => Mql.Convert(x.DoubleProperty, new ConvertOptions { ByteOrder = byteOrder, SubType = BsonBinarySubType.Binary })); var expectedStages = new[] { $"{{ $match : {{ _id : {id} }} }}", - $"{{ $project: {{ _v : {{ $convert : {{ input : '$IntProperty', to : {{ type: 'binData', subtype: 0 }}, {ByteOrderToString(byteOrder)} }} }}, _id : 0 }} }}", + $"{{ $project: {{ _v : {{ $convert : {{ input : '$DoubleProperty', to : {{ type: 'binData', subtype: 0 }}, {ByteOrderToString(byteOrder)} }} }}, _id : 0 }} }}", }; - var expectedResult = expectedBase64 == null ? default : new BsonBinaryData(Convert.FromBase64String(expectedBase64)); - AssertOutcome(collection, queryable, expectedStages, expectedResult, expectedException); + var expectedResult = new BsonBinaryData(Convert.FromBase64String(expectedBase64)); + AssertOutcome(collection, queryable, expectedStages, expectedResult); } [Theory] - [InlineData(2, ByteOrder.LittleEndian, 0, "MongoCommandException")] - [InlineData(4, ByteOrder.LittleEndian, 674, null)] - [InlineData(6, ByteOrder.BigEndian, 42, null)] - public void Convert_to_int_from_BsonBinaryData_should_work(int id, ByteOrder byteOrder, int expectedResult, string expectedException) + [InlineData(BsonBinarySubType.Binary, 0)] + [InlineData(BsonBinarySubType.Sensitive, 8)] + [InlineData(BsonBinarySubType.UserDefined, 128)] + public void Convert_with_subtype_should_be_rendered_correctly(BsonBinarySubType subType, int expectedSubTypeValue) { - RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); + RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromString); + const int id = 3; var collection = Fixture.Collection; var queryable = collection.AsQueryable() .Where(x => x.Id == id) - .Select(x => Mql.Convert(x.BinaryProperty, new ConvertOptions { ByteOrder = byteOrder })); + .Select(x => Mql.Convert(x.DoubleProperty, new ConvertOptions { ByteOrder = ByteOrder.LittleEndian, SubType = subType })); var expectedStages = new[] { $"{{ $match : {{ _id : {id} }} }}", - $"{{ $project: {{ _v : {{ $convert : {{ input : '$BinaryProperty', to : 'int', {ByteOrderToString(byteOrder)} }} }}, _id : 0 }} }}", + $"{{ $project: {{ _v : {{ $convert : {{ input : '$DoubleProperty', to : {{ type: 'binData', subtype: {expectedSubTypeValue} }}, 'byteOrder': 'little' }} }}, _id : 0 }} }}", }; - AssertOutcome(collection, queryable, expectedStages, expectedResult, expectedException); + var expectedResult = new BsonBinaryData(Convert.FromBase64String("AAAAAAAA4L8="), subType); + AssertOutcome(collection, queryable, expectedStages, expectedResult); } - [Theory] - [InlineData(4, ByteOrder.LittleEndian,"ogIAAA==", null)] - [InlineData(6, ByteOrder.BigEndian, "AAAAKg==", null )] - [InlineData(10, ByteOrder.BigEndian, null, "MongoCommandException")] - public void Convert_to_BsonBinaryData_from_long_should_work(int id, ByteOrder byteOrder, string expectedBase64, string expectedException) + [Fact] + public void Convert_without_options_should_be_reduced_if_possible() { RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromString); + const int id = 21; var collection = Fixture.Collection; var queryable = collection.AsQueryable() .Where(x => x.Id == id) - .Select(x => Mql.Convert(x.LongProperty, new ConvertOptions { ByteOrder = byteOrder, SubType = BsonBinarySubType.Binary })); + .Select(x => Mql.Convert(x.StringProperty, null)); var expectedStages = new[] { $"{{ $match : {{ _id : {id} }} }}", - $"{{ $project: {{ _v : {{ $convert : {{ input : '$LongProperty', to : {{ type: 'binData', subtype: 0 }}, {ByteOrderToString(byteOrder)} }} }}, _id : 0 }} }}", + $"{{ $project: {{ _v : {{ $toInt : '$StringProperty' }}, _id : 0 }} }}", }; - BsonBinaryData expectedResult = null; - - if (expectedBase64 is not null) - { - //$convert to BinData returns always 8 bytes when from long - var expectedBytes = new byte[8]; - Array.Copy(Convert.FromBase64String(expectedBase64), 0, expectedBytes, byteOrder is ByteOrder.LittleEndian ? 0 : 4, 4); - expectedResult = new BsonBinaryData(expectedBytes); - } - - AssertOutcome(collection, queryable, expectedStages, expectedResult, expectedException); + AssertOutcome(collection, queryable, expectedStages, 15); } - [Theory] - [InlineData(2, ByteOrder.LittleEndian, 0, "MongoCommandException")] - [InlineData(4, ByteOrder.LittleEndian, (long)674, null)] - [InlineData(6, ByteOrder.BigEndian, (long)42, null)] - public void Convert_to_long_from_BsonBinaryData_should_work(int id, ByteOrder byteOrder, long expectedResult, string expectedException) + [Fact] + public void Convert_with_empty_options_should_be_reduced_if_possible() { - RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); + RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromString); + const int id = 21; var collection = Fixture.Collection; var queryable = collection.AsQueryable() .Where(x => x.Id == id) - .Select(x => Mql.Convert(x.BinaryProperty, new ConvertOptions { ByteOrder = byteOrder })); + .Select(x => Mql.Convert(x.StringProperty, new ConvertOptions())); var expectedStages = new[] { $"{{ $match : {{ _id : {id} }} }}", - $"{{ $project: {{ _v : {{ $convert : {{ input : '$BinaryProperty', to : 'long', {ByteOrderToString(byteOrder)} }} }}, _id : 0 }} }}", + $"{{ $project: {{ _v : {{ $toInt : '$StringProperty' }}, _id : 0 }} }}", }; - AssertOutcome(collection, queryable, expectedStages, expectedResult, expectedException); + AssertOutcome(collection, queryable, expectedStages, 15); } private void AssertOutcome(IMongoCollection collection, @@ -432,4 +453,4 @@ public class TestClass public string StringProperty { get; set; } } } -} \ No newline at end of file +} From 03c397481ae47960310198519782af502fa01f64 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Thu, 3 Apr 2025 15:49:15 +0200 Subject: [PATCH 67/82] Slight improvements --- .../Linq/Linq3Implementation/Reflection/MqlMethod.cs | 6 +++--- src/MongoDB.Driver/Mql.cs | 10 ++++------ ...vertMethodToAggregationExpressionTranslatorTests.cs | 3 --- 3 files changed, 7 insertions(+), 12 deletions(-) diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/MqlMethod.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/MqlMethod.cs index a5bdcaa6718..02916395b57 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/MqlMethod.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/MqlMethod.cs @@ -23,9 +23,9 @@ namespace MongoDB.Driver.Linq.Linq3Implementation.Reflection internal static class MqlMethod { // private static fields - private static readonly MethodInfo __convert; private static readonly MethodInfo __constantWithRepresentation; private static readonly MethodInfo __constantWithSerializer; + private static readonly MethodInfo __convert; private static readonly MethodInfo __dateFromString; private static readonly MethodInfo __dateFromStringWithFormat; private static readonly MethodInfo __dateFromStringWithFormatAndTimezone; @@ -38,9 +38,9 @@ internal static class MqlMethod // static constructor static MqlMethod() { - __convert = ReflectionInfo.Method((object value, ConvertOptions options) => Mql.Convert(value, options)); __constantWithRepresentation = ReflectionInfo.Method((object value, BsonType representation) => Mql.Constant(value, representation)); __constantWithSerializer = ReflectionInfo.Method((object value, IBsonSerializer serializer) => Mql.Constant(value, serializer)); + __convert = ReflectionInfo.Method((object value, ConvertOptions options) => Mql.Convert(value, options)); __dateFromString = ReflectionInfo.Method((string dateStringl) => Mql.DateFromString(dateStringl)); __dateFromStringWithFormat = ReflectionInfo.Method((string dateString, string format) => Mql.DateFromString(dateString, format)); __dateFromStringWithFormatAndTimezone = ReflectionInfo.Method((string dateString, string format, string timezone) => Mql.DateFromString(dateString, format, timezone)); @@ -52,9 +52,9 @@ static MqlMethod() } // public properties - public static MethodInfo Convert => __convert; public static MethodInfo ConstantWithRepresentation => __constantWithRepresentation; public static MethodInfo ConstantWithSerializer => __constantWithSerializer; + public static MethodInfo Convert => __convert; public static MethodInfo DateFromString => __dateFromString; public static MethodInfo DateFromStringWithFormat => __dateFromStringWithFormat; public static MethodInfo DateFromStringWithFormatAndTimezone => __dateFromStringWithFormatAndTimezone; diff --git a/src/MongoDB.Driver/Mql.cs b/src/MongoDB.Driver/Mql.cs index d635ee686ca..e37d3c7f110 100644 --- a/src/MongoDB.Driver/Mql.cs +++ b/src/MongoDB.Driver/Mql.cs @@ -50,7 +50,6 @@ public static TValue Constant(TValue value, BsonType representaion) throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } - /// /// Converts a value from one type to another using the $convert aggregation operator. /// @@ -59,7 +58,7 @@ public static TValue Constant(TValue value, BsonType representaion) /// The value to convert. /// The conversion options. /// The converted value. - /// Thrown when the method is not supported. + /// Not all conversions are supported by the $convert operator. public static TTo Convert(TFrom value, ConvertOptions options) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); @@ -171,7 +170,7 @@ public static bool IsNullOrMissing(TField field) } /// - /// Represents the byte order of binData when converting to/from numerical types. + /// Represents the byte order of binary data when converting to/from numerical types using . /// public enum ByteOrder { @@ -186,7 +185,7 @@ public enum ByteOrder } /// - /// Represents the typed options parameter for . + /// Represents the options parameter for . /// public abstract class ConvertOptions { @@ -230,9 +229,8 @@ public BsonBinarySubType? SubType internal abstract BsonValue GetOnNull(); } - /// - /// Represents the typed options parameter for . + /// Represents the options parameter for . /// This class allows to set 'onError' and 'onNull'. /// /// The type of 'onError' and 'onNull'. diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs index f03430614c3..e2b773c40b0 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs @@ -447,9 +447,6 @@ public class TestClass public double DoubleProperty { get; set; } public int IntProperty { get; set; } public long LongProperty { get; set; } - public double? NullableDoubleProperty { get; set; } - public int? NullableIntProperty { get; set; } - public long? NullableLongProperty { get; set; } public string StringProperty { get; set; } } } From 9aeabb31dc78906eb99ea7e76a1b40a51cb1d953 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Fri, 4 Apr 2025 11:24:59 +0200 Subject: [PATCH 68/82] Small fix --- .../ConvertMethodToAggregationExpressionTranslator.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs index 4b15edb84a2..4eb7b2a7661 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs @@ -140,7 +140,7 @@ private static T GetConstantValue(Expression expression, string fieldName) return (T)constantExpression.Value; } - private static BsonType GetBsonType(Type type) //TODO Do we have this kind of info somewhere else...? + private static BsonType GetBsonType(Type type) { return Type.GetTypeCode(Nullable.GetUnderlyingType(type) ?? type) switch { @@ -161,6 +161,7 @@ private static BsonType GetBsonType(Type type) //TODO Do we have this kind of i TypeCode.DateTime => BsonType.DateTime, TypeCode.Object when type == typeof(byte[]) => BsonType.Binary, TypeCode.Object when type == typeof(BsonBinaryData) => BsonType.Binary, + _ when type == typeof(Decimal128) => BsonType.Decimal128, _ when type == typeof(Guid) => BsonType.Binary, _ when type == typeof(ObjectId) => BsonType.ObjectId, _ => BsonType.Document From 07de4e5b1af1a74d436bafb5e086728009cf06b7 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Fri, 4 Apr 2025 11:25:17 +0200 Subject: [PATCH 69/82] Removed constraints --- ...MethodToAggregationExpressionTranslatorTests.cs | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs index e2b773c40b0..a93f7dcbf38 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs @@ -187,8 +187,6 @@ public void Convert_to_double_from_BsonBinaryData_should_work(int id, ByteOrder [InlineData(25)] public void Convert_with_constant_OnError_should_work(int? onError) { - RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromString); - const int id = 20; var collection = Fixture.Collection; var queryable = collection.AsQueryable() @@ -211,8 +209,6 @@ public void Convert_with_constant_OnError_should_work(int? onError) [InlineData(25)] public void Convert_with_constant_OnNull_should_work(int? onNull) { - RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromString); - const int id = 0; var collection = Fixture.Collection; var queryable = collection.AsQueryable() @@ -233,8 +229,6 @@ public void Convert_with_constant_OnNull_should_work(int? onNull) [Fact] public void Convert_with_field_OnError_should_work() { - RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromString); - const int id = 20; var collection = Fixture.Collection; var queryable = collection.AsQueryable() @@ -254,8 +248,6 @@ public void Convert_with_field_OnError_should_work() [Fact] public void Convert_with_field_OnNull_should_work() { - RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromString); - const int id = 22; var collection = Fixture.Collection; var queryable = collection.AsQueryable() @@ -344,10 +336,8 @@ public void Convert_with_subtype_should_be_rendered_correctly(BsonBinarySubType } [Fact] - public void Convert_without_options_should_be_reduced_if_possible() + public void Convert_with_null_options_should_be_reduced_if_possible() { - RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromString); - const int id = 21; var collection = Fixture.Collection; var queryable = collection.AsQueryable() @@ -367,8 +357,6 @@ public void Convert_without_options_should_be_reduced_if_possible() [Fact] public void Convert_with_empty_options_should_be_reduced_if_possible() { - RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromString); - const int id = 21; var collection = Fixture.Collection; var queryable = collection.AsQueryable() From 39904d0deb106beb815f4aeb1a7d3270fe6dd883 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Fri, 4 Apr 2025 11:25:29 +0200 Subject: [PATCH 70/82] Tried to add new tests --- ...dToAggregationExpressionTranslatorTests.cs | 165 ++++++++++++++++++ 1 file changed, 165 insertions(+) diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs index a93f7dcbf38..5413276a795 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs @@ -16,6 +16,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Linq.Expressions; using MongoDB.Bson; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.TestHelpers.XunitExtensions; @@ -373,6 +374,170 @@ public void Convert_with_empty_options_should_be_reduced_if_possible() AssertOutcome(collection, queryable, expectedStages, 15); } + [Fact] + public void Convert_with_constant_should_work() + { + const int id = 21; + var collection = Fixture.Collection; + var queryable = collection.AsQueryable() + .Where(x => x.Id == id) + .Select(x => Mql.Convert("123", new ConvertOptions())); + + var expectedStages = + new[] + { + $"{{ $match : {{ _id : {id} }} }}", + $"{{ $project: {{ _v : {{ $toInt : '123' }}, _id : 0 }} }}", + }; + + AssertOutcome(collection, queryable, expectedStages, 123); + } + + [Fact] + public void Test() + { + Expression> expression = x => Mql.Convert(x.IntProperty, null); + + const int id = 22; + var collection = Fixture.Collection; + var queryable = collection.AsQueryable() + .Where(x => x.Id == id) + .Select(expression); + + var expectedStages = + new[] + { + $"{{ $match : {{ _id : {id} }} }}", + $"{{ $project: {{ _v : {{ $toBool : '$IntProperty' }}, _id : 0 }} }}", + }; + + AssertOutcome(collection, queryable, expectedStages, true); + } + + + [Fact] + public void Convert_to_boolean_should_work() + { + const int id = 22; + var collection = Fixture.Collection; + var queryable = collection.AsQueryable() + .Where(x => x.Id == id) + .Select(x => Mql.Convert(x.IntProperty, null)); + + var expectedStages = + new[] + { + $"{{ $match : {{ _id : {id} }} }}", + $"{{ $project: {{ _v : {{ $toBool : '$IntProperty' }}, _id : 0 }} }}", + }; + + AssertOutcome(collection, queryable, expectedStages, true); + } + + [Fact] + public void Convert_to_integer_should_work() + { + const int id = 21; + var collection = Fixture.Collection; + var queryable = collection.AsQueryable() + .Where(x => x.Id == id) + .Select(x => Mql.Convert(x.StringProperty, null)); + + var expectedStages = + new[] + { + $"{{ $match : {{ _id : {id} }} }}", + $"{{ $project: {{ _v : {{ $toInt : '$StringProperty' }}, _id : 0 }} }}", + }; + + AssertOutcome(collection, queryable, expectedStages, 15); + } + + [Fact] + public void Convert_to_decimal_should_work() + { + const int id = 22; + var collection = Fixture.Collection; + var queryable = collection.AsQueryable() + .Where(x => x.Id == id) + .Select(x => Mql.Convert(x.IntProperty, null)); + + var expectedStages = + new[] + { + $"{{ $match : {{ _id : {id} }} }}", + $"{{ $project: {{ _v : {{ $toDecimal : '$IntProperty' }}, _id : 0 }} }}", + }; + + AssertOutcome(collection, queryable, expectedStages, 33); + } + + [Fact] + public void Convert_to_decimal128_should_work() + { + const int id = 22; + var collection = Fixture.Collection; + var queryable = collection.AsQueryable() + .Where(x => x.Id == id) + .Select(x => Mql.Convert(x.IntProperty, null)); + + var expectedStages = + new[] + { + $"{{ $match : {{ _id : {id} }} }}", + $"{{ $project: {{ _v : {{ $toDecimal : '$IntProperty' }}, _id : 0 }} }}", + }; + + AssertOutcome(collection, queryable, expectedStages, 33); + } + + [Theory] + [InlineData(0, null)] + [InlineData(21, 15)] + public void Convert_to_nullable_value_type_should_work(int id, int? expectedResult) + { + var collection = Fixture.Collection; + var queryable = collection.AsQueryable() + .Where(x => x.Id == id) + .Select(x => Mql.Convert(x.StringProperty, null)); + + var expectedStages = + new[] + { + $"{{ $match : {{ _id : {id} }} }}", + $"{{ $project: {{ _v : {{ $toInt : '$StringProperty' }}, _id : 0 }} }}", + }; + + AssertOutcome(collection, queryable, expectedStages, expectedResult); + } + + + public static IEnumerable ConvertTestData => new List + { + new object[] { 22, (Expression>)(x => Mql.Convert(x.IntProperty, null)), "{ $project: { _v : { $toBool : '$IntProperty' }, _id : 0 } }", true }, + new object[] { 22, (Expression>)(x => Mql.Convert(x.IntProperty, null)), "{ $project: { _v : { $toDecimal : '$IntProperty' }, _id : 0 } }", 33m }, + new object[] { 22, (Expression>)(x => Mql.Convert(x.IntProperty, null)), "{ $project: { _v : { $toDecimal : '$IntProperty' }, _id : 0 } }", new Decimal128(33) } + }; + + [Theory] + [MemberData(nameof(ConvertTestData))] + public void Convert_should_work( + int id, + Expression> projection, + string expectedStage, + object expectedValue) + { + var collection = Fixture.Collection; + var queryable = collection.AsQueryable() + .Where(x => x.Id == id) + .Select(projection); + + var expectedStages = new[] { $"{{ $match : {{ _id : {id} }} }}", expectedStage }; + + AssertOutcome(collection, queryable, expectedStages, expectedValue); + } + + private void AssertOutcome(IMongoCollection collection, IQueryable queryable, string[] expectedStages, From e4fc2a7818917de3468651f6d7a3137f97405d3b Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Fri, 4 Apr 2025 11:52:15 +0200 Subject: [PATCH 71/82] Added generic convert test --- ...dToAggregationExpressionTranslatorTests.cs | 155 ++++++------------ 1 file changed, 52 insertions(+), 103 deletions(-) diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs index 5413276a795..117c41bacbd 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs @@ -393,104 +393,6 @@ public void Convert_with_constant_should_work() AssertOutcome(collection, queryable, expectedStages, 123); } - [Fact] - public void Test() - { - Expression> expression = x => Mql.Convert(x.IntProperty, null); - - const int id = 22; - var collection = Fixture.Collection; - var queryable = collection.AsQueryable() - .Where(x => x.Id == id) - .Select(expression); - - var expectedStages = - new[] - { - $"{{ $match : {{ _id : {id} }} }}", - $"{{ $project: {{ _v : {{ $toBool : '$IntProperty' }}, _id : 0 }} }}", - }; - - AssertOutcome(collection, queryable, expectedStages, true); - } - - - [Fact] - public void Convert_to_boolean_should_work() - { - const int id = 22; - var collection = Fixture.Collection; - var queryable = collection.AsQueryable() - .Where(x => x.Id == id) - .Select(x => Mql.Convert(x.IntProperty, null)); - - var expectedStages = - new[] - { - $"{{ $match : {{ _id : {id} }} }}", - $"{{ $project: {{ _v : {{ $toBool : '$IntProperty' }}, _id : 0 }} }}", - }; - - AssertOutcome(collection, queryable, expectedStages, true); - } - - [Fact] - public void Convert_to_integer_should_work() - { - const int id = 21; - var collection = Fixture.Collection; - var queryable = collection.AsQueryable() - .Where(x => x.Id == id) - .Select(x => Mql.Convert(x.StringProperty, null)); - - var expectedStages = - new[] - { - $"{{ $match : {{ _id : {id} }} }}", - $"{{ $project: {{ _v : {{ $toInt : '$StringProperty' }}, _id : 0 }} }}", - }; - - AssertOutcome(collection, queryable, expectedStages, 15); - } - - [Fact] - public void Convert_to_decimal_should_work() - { - const int id = 22; - var collection = Fixture.Collection; - var queryable = collection.AsQueryable() - .Where(x => x.Id == id) - .Select(x => Mql.Convert(x.IntProperty, null)); - - var expectedStages = - new[] - { - $"{{ $match : {{ _id : {id} }} }}", - $"{{ $project: {{ _v : {{ $toDecimal : '$IntProperty' }}, _id : 0 }} }}", - }; - - AssertOutcome(collection, queryable, expectedStages, 33); - } - - [Fact] - public void Convert_to_decimal128_should_work() - { - const int id = 22; - var collection = Fixture.Collection; - var queryable = collection.AsQueryable() - .Where(x => x.Id == id) - .Select(x => Mql.Convert(x.IntProperty, null)); - - var expectedStages = - new[] - { - $"{{ $match : {{ _id : {id} }} }}", - $"{{ $project: {{ _v : {{ $toDecimal : '$IntProperty' }}, _id : 0 }} }}", - }; - - AssertOutcome(collection, queryable, expectedStages, 33); - } - [Theory] [InlineData(0, null)] [InlineData(21, 15)] @@ -511,12 +413,58 @@ public void Convert_to_nullable_value_type_should_work(int id, int? expectedResu AssertOutcome(collection, queryable, expectedStages, expectedResult); } - public static IEnumerable ConvertTestData => new List { - new object[] { 22, (Expression>)(x => Mql.Convert(x.IntProperty, null)), "{ $project: { _v : { $toBool : '$IntProperty' }, _id : 0 } }", true }, - new object[] { 22, (Expression>)(x => Mql.Convert(x.IntProperty, null)), "{ $project: { _v : { $toDecimal : '$IntProperty' }, _id : 0 } }", 33m }, - new object[] { 22, (Expression>)(x => Mql.Convert(x.IntProperty, null)), "{ $project: { _v : { $toDecimal : '$IntProperty' }, _id : 0 } }", new Decimal128(33) } + // To binData + new object[] { 4, (Expression>)(x => Mql.Convert(x.IntProperty, new ConvertOptions { SubType = BsonBinarySubType.Binary, ByteOrder = ByteOrder.LittleEndian })), + "{ $project: { _v : { $convert : { input : '$IntProperty', to : { type: 'binData', subtype: 0 }, byteOrder : 'little' }, _id : 0 } } }", + new BsonBinaryData(Convert.FromBase64String("ogIAAA==")) }, + + // To objectId + new object[] { 24, (Expression>)(x => Mql.Convert(x.StringProperty, null)), + "{ $project: { _v : { $toObjectId : '$StringProperty' }, _id : 0 } }", + ObjectId.Parse("5ab9cbfa31c2ab715d42129e") }, + + // To date + new object[] { 23, (Expression>)(x => Mql.Convert(x.StringProperty, null)), + "{ $project: { _v : { $toDate : '$StringProperty' }, _id : 0 } }", + new DateTime(2018, 03, 03) }, + + // To string + new object[] { 22, (Expression>)(x => Mql.Convert(x.IntProperty, null)), + "{ $project: { _v : { $toString : '$IntProperty' }, _id : 0 } }", + "33" }, + + // To int + new object[] { 22, (Expression>)(x => Mql.Convert(x.IntProperty, null)), + "{ $project: { _v : { $toInt : '$IntProperty' }, _id : 0 } }", + 33 }, + + // To long + new object[] { 22, (Expression>)(x => Mql.Convert(x.IntProperty, null)), + "{ $project: { _v : { $toLong : '$IntProperty' }, _id : 0 } }", + (long)33 }, + + // To bool + new object[] { 22, (Expression>)(x => Mql.Convert(x.IntProperty, null)), + "{ $project: { _v : { $toBool : '$IntProperty' }, _id : 0 } }", + true }, + + // To decimal + new object[] { 22, (Expression>)(x => Mql.Convert(x.IntProperty, null)), + "{ $project: { _v : { $toDecimal : '$IntProperty' }, _id : 0 } }", + 33m }, + new object[] { 22, (Expression>)(x => Mql.Convert(x.IntProperty, null)), + "{ $project: { _v : { $toDecimal : '$IntProperty' }, _id : 0 } }", + new Decimal128(33) }, + + // To float/double + new object[] { 22, (Expression>)(x => Mql.Convert(x.IntProperty, null)), + "{ $project: { _v : { $toDouble : '$IntProperty' }, _id : 0 } }", + (float)33.0 }, + new object[] { 22, (Expression>)(x => Mql.Convert(x.IntProperty, null)), + "{ $project: { _v : { $toDouble : '$IntProperty' }, _id : 0 } }", + 33.0 }, }; [Theory] @@ -537,7 +485,6 @@ public void Convert_should_work( AssertOutcome(collection, queryable, expectedStages, expectedValue); } - private void AssertOutcome(IMongoCollection collection, IQueryable queryable, string[] expectedStages, @@ -590,6 +537,8 @@ public sealed class ClassFixture : MongoCollectionFixture Date: Fri, 4 Apr 2025 12:13:11 +0200 Subject: [PATCH 72/82] Small fix --- ...dToAggregationExpressionTranslatorTests.cs | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs index 117c41bacbd..a549849f739 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs @@ -415,26 +415,6 @@ public void Convert_to_nullable_value_type_should_work(int id, int? expectedResu public static IEnumerable ConvertTestData => new List { - // To binData - new object[] { 4, (Expression>)(x => Mql.Convert(x.IntProperty, new ConvertOptions { SubType = BsonBinarySubType.Binary, ByteOrder = ByteOrder.LittleEndian })), - "{ $project: { _v : { $convert : { input : '$IntProperty', to : { type: 'binData', subtype: 0 }, byteOrder : 'little' }, _id : 0 } } }", - new BsonBinaryData(Convert.FromBase64String("ogIAAA==")) }, - - // To objectId - new object[] { 24, (Expression>)(x => Mql.Convert(x.StringProperty, null)), - "{ $project: { _v : { $toObjectId : '$StringProperty' }, _id : 0 } }", - ObjectId.Parse("5ab9cbfa31c2ab715d42129e") }, - - // To date - new object[] { 23, (Expression>)(x => Mql.Convert(x.StringProperty, null)), - "{ $project: { _v : { $toDate : '$StringProperty' }, _id : 0 } }", - new DateTime(2018, 03, 03) }, - - // To string - new object[] { 22, (Expression>)(x => Mql.Convert(x.IntProperty, null)), - "{ $project: { _v : { $toString : '$IntProperty' }, _id : 0 } }", - "33" }, - // To int new object[] { 22, (Expression>)(x => Mql.Convert(x.IntProperty, null)), "{ $project: { _v : { $toInt : '$IntProperty' }, _id : 0 } }", @@ -465,6 +445,26 @@ public void Convert_to_nullable_value_type_should_work(int id, int? expectedResu new object[] { 22, (Expression>)(x => Mql.Convert(x.IntProperty, null)), "{ $project: { _v : { $toDouble : '$IntProperty' }, _id : 0 } }", 33.0 }, + + // To objectId + new object[] { 24, (Expression>)(x => Mql.Convert(x.StringProperty, null)), + "{ $project: { _v : { $toObjectId : '$StringProperty' }, _id : 0 } }", + ObjectId.Parse("5ab9cbfa31c2ab715d42129e") }, + + // To date + new object[] { 23, (Expression>)(x => Mql.Convert(x.StringProperty, null)), + "{ $project: { _v : { $toDate : '$StringProperty' }, _id : 0 } }", + new DateTime(2018, 03, 03) }, + + // To string + new object[] { 22, (Expression>)(x => Mql.Convert(x.IntProperty, null)), + "{ $project: { _v : { $toString : '$IntProperty' }, _id : 0 } }", + "33" }, + + // To binData + new object[] { 4, (Expression>)(x => Mql.Convert(x.IntProperty, new ConvertOptions { SubType = BsonBinarySubType.Binary, ByteOrder = ByteOrder.LittleEndian })), + "{ $project: { _v : { $convert : { input : '$IntProperty', to : { type: 'binData', subtype: 0 }, byteOrder : 'little' }, _id : 0 } } }", + new BsonBinaryData(Convert.FromBase64String("ogIAAA==")) }, }; [Theory] From 276434dc10121d625d69bf684f84c4ca0137fb79 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Mon, 7 Apr 2025 10:12:47 +0200 Subject: [PATCH 73/82] Small fixes --- src/MongoDB.Driver/ConvertOptions.cs | 139 ++++++++++++++++++ .../Ast/Expressions/AstExpression.cs | 10 +- src/MongoDB.Driver/Mql.cs | 117 --------------- 3 files changed, 146 insertions(+), 120 deletions(-) create mode 100644 src/MongoDB.Driver/ConvertOptions.cs diff --git a/src/MongoDB.Driver/ConvertOptions.cs b/src/MongoDB.Driver/ConvertOptions.cs new file mode 100644 index 00000000000..75a5f0b1a72 --- /dev/null +++ b/src/MongoDB.Driver/ConvertOptions.cs @@ -0,0 +1,139 @@ +/* Copyright 2010-present MongoDB Inc. + * + * 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 MongoDB.Bson; +using MongoDB.Bson.Serialization; +using MongoDB.Driver.Linq.Linq3Implementation.Serializers; + +namespace MongoDB.Driver +{ + /// + /// Represents the options parameter for . + /// + public abstract class ConvertOptions + { + private ByteOrder? _byteOrder; + private string _format; + private BsonBinarySubType? _subType; + + /// + /// The byteOrder parameter. + /// + public ByteOrder? ByteOrder + { + get => _byteOrder; + set => _byteOrder = value; + } + + /// + /// The format parameter. + /// + public string Format + { + get => _format; + set => _format = value; + } + + /// + /// The subType parameter. + /// + public BsonBinarySubType? SubType + { + get => _subType; + set => _subType = value; + } + + internal abstract bool OnErrorWasSet(out object onError); + + internal abstract bool OnNullWasSet(out object onError); + } + + /// + /// Represents the options parameter for . + /// This class allows to set 'onError' and 'onNull'. + /// + /// The type of 'onError' and 'onNull'. + public class ConvertOptions : ConvertOptions + { + private TTo _onError; + private bool _onErrorWasSet; + private TTo _onNull; + private bool _onNullWasSet; + private readonly IBsonSerializer _serializer; + + /// + /// Initializes a new instance of the class. + /// + public ConvertOptions() + { + _serializer = StandardSerializers.TryGetSerializer(typeof(TTo), out var serializer) + ? serializer + : BsonSerializer.LookupSerializer(typeof(TTo)); + } + + /// + /// The onError parameter. + /// + public TTo OnError + { + get => _onError; + set + { + _onError = value; + _onErrorWasSet = true; + } + } + + /// + /// The onNull parameter. + /// + public TTo OnNull + { + get => _onNull; + set + { + _onNull = value; + _onNullWasSet = true; + } + } + + internal override bool OnErrorWasSet(out object onError) + { + onError = _onError; + return _onErrorWasSet; + } + + internal override bool OnNullWasSet(out object onNull) + { + onNull = _onNull; + return _onNullWasSet; + } + } + + /// + /// Represents the byte order of binary data when converting to/from numerical types using . + /// + public enum ByteOrder + { + /// + /// Big endian order. + /// + BigEndian, + /// + /// Little endian order. + /// + LittleEndian, + } +} \ No newline at end of file diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstExpression.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstExpression.cs index 8de612f95f1..fa42427419a 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstExpression.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstExpression.cs @@ -267,9 +267,13 @@ public static AstExpression Convert( Ensure.IsNotNull(input, nameof(input)); Ensure.IsNotNull(to, nameof(to)); - if (onError == null && onNull == null && subType == null && format == null && byteOrder == null && - to is AstConstantExpression toConstantExpression && - (toConstantExpression.Value as BsonString)?.Value is { } toValue) + if (to is AstConstantExpression toConstantExpression && + (toConstantExpression.Value as BsonString)?.Value is { } toValue && + subType == null && + byteOrder == null && + format == null && + onError == null && + onNull == null) { var unaryOperator = toValue switch { diff --git a/src/MongoDB.Driver/Mql.cs b/src/MongoDB.Driver/Mql.cs index e37d3c7f110..d0e0df4c106 100644 --- a/src/MongoDB.Driver/Mql.cs +++ b/src/MongoDB.Driver/Mql.cs @@ -168,121 +168,4 @@ public static bool IsNullOrMissing(TField field) throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } } - - /// - /// Represents the byte order of binary data when converting to/from numerical types using . - /// - public enum ByteOrder - { - /// - /// Big endian order. - /// - BigEndian, - /// - /// Little endian order. - /// - LittleEndian, - } - - /// - /// Represents the options parameter for . - /// - public abstract class ConvertOptions - { - private ByteOrder? _byteOrder; - private string _format; - private BsonBinarySubType? _subType; - - /// - /// The byteOrder parameter. - /// - public ByteOrder? ByteOrder - { - get => _byteOrder; - set => _byteOrder = value; - } - - /// - /// The format parameter. - /// - public string Format - { - get => _format; - set => _format = value; - } - - /// - /// The subType parameter. - /// - public BsonBinarySubType? SubType - { - get => _subType; - set => _subType = value; - } - - internal abstract bool OnErrorWasSet { get; } - - internal abstract bool OnNullWasSet { get; } - - internal abstract BsonValue GetOnError(); - - internal abstract BsonValue GetOnNull(); - } - - /// - /// Represents the options parameter for . - /// This class allows to set 'onError' and 'onNull'. - /// - /// The type of 'onError' and 'onNull'. - public class ConvertOptions : ConvertOptions - { - private TTo _onError; - private bool _onErrorWasSet; - private TTo _onNull; - private bool _onNullWasSet; - private readonly IBsonSerializer _serializer; - - /// - /// Initializes a new instance of the class. - /// - public ConvertOptions() - { - _serializer = StandardSerializers.TryGetSerializer(typeof(TTo), out var serializer) - ? serializer - : BsonSerializer.LookupSerializer(typeof(TTo)); - } - - /// - /// The onError parameter. - /// - public TTo OnError - { - get => _onError; - set - { - _onError = value; - _onErrorWasSet = true; - } - } - - /// - /// The onNull parameter. - /// - public TTo OnNull - { - get => _onNull; - set - { - _onNull = value; - _onNullWasSet = true; - } - } - - internal override bool OnErrorWasSet => _onErrorWasSet; - internal override bool OnNullWasSet => _onNullWasSet; - - internal override BsonValue GetOnError() => _serializer.ToBsonValue(_onError); - - internal override BsonValue GetOnNull() => _serializer.ToBsonValue(_onNull); - } } From 23de8698c0a752c595281a56f5903d2414d5f9a9 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Mon, 7 Apr 2025 10:49:08 +0200 Subject: [PATCH 74/82] Fixes according to PR --- .../Serializers/ByteSerializer.cs | 10 + .../Serializers/Int16Serializer.cs | 9 + .../Serializers/SByteSerializer.cs | 9 + .../Serializers/UInt16Serializer.cs | 9 + .../Serializers/UInt64Serializer.cs | 9 + .../Misc/SerializationHelper.cs | 5 + ...MethodToAggregationExpressionTranslator.cs | 186 +++++++++--------- 7 files changed, 147 insertions(+), 90 deletions(-) diff --git a/src/MongoDB.Bson/Serialization/Serializers/ByteSerializer.cs b/src/MongoDB.Bson/Serialization/Serializers/ByteSerializer.cs index 1f27a5aeeb1..f8043edbe3a 100644 --- a/src/MongoDB.Bson/Serialization/Serializers/ByteSerializer.cs +++ b/src/MongoDB.Bson/Serialization/Serializers/ByteSerializer.cs @@ -23,6 +23,16 @@ namespace MongoDB.Bson.Serialization.Serializers /// public sealed class ByteSerializer : StructSerializerBase, IRepresentationConfigurable { + #region static + private static readonly ByteSerializer __instance = new(); + + // public static properties + /// + /// Gets a cached instance of a default ByteSerializer. + /// + public static ByteSerializer Instance => __instance; + #endregion + // private fields private readonly BsonType _representation; diff --git a/src/MongoDB.Bson/Serialization/Serializers/Int16Serializer.cs b/src/MongoDB.Bson/Serialization/Serializers/Int16Serializer.cs index 0b9c2a1ce50..90679385bde 100644 --- a/src/MongoDB.Bson/Serialization/Serializers/Int16Serializer.cs +++ b/src/MongoDB.Bson/Serialization/Serializers/Int16Serializer.cs @@ -24,6 +24,15 @@ namespace MongoDB.Bson.Serialization.Serializers /// public sealed class Int16Serializer : StructSerializerBase, IRepresentationConfigurable, IRepresentationConverterConfigurable { + #region static + private static readonly Int16Serializer __instance = new(); + + /// + /// Gets a cached instance of an Int16Serializer; + /// + public static Int16Serializer Instance => __instance; + #endregion + // private fields private readonly BsonType _representation; private readonly RepresentationConverter _converter; diff --git a/src/MongoDB.Bson/Serialization/Serializers/SByteSerializer.cs b/src/MongoDB.Bson/Serialization/Serializers/SByteSerializer.cs index 4245ff29dfa..9065a778e96 100644 --- a/src/MongoDB.Bson/Serialization/Serializers/SByteSerializer.cs +++ b/src/MongoDB.Bson/Serialization/Serializers/SByteSerializer.cs @@ -24,6 +24,15 @@ namespace MongoDB.Bson.Serialization.Serializers [CLSCompliant(false)] public sealed class SByteSerializer : StructSerializerBase, IRepresentationConfigurable { + #region static + private static readonly SByteSerializer __instance = new(); + + /// + /// Gets a cached instance of an SByteSerializer; + /// + public static SByteSerializer Instance => __instance; + #endregion + // private fields private readonly BsonType _representation; diff --git a/src/MongoDB.Bson/Serialization/Serializers/UInt16Serializer.cs b/src/MongoDB.Bson/Serialization/Serializers/UInt16Serializer.cs index dd75fb4190f..6e1bc6dca35 100644 --- a/src/MongoDB.Bson/Serialization/Serializers/UInt16Serializer.cs +++ b/src/MongoDB.Bson/Serialization/Serializers/UInt16Serializer.cs @@ -25,6 +25,15 @@ namespace MongoDB.Bson.Serialization.Serializers [CLSCompliant(false)] public sealed class UInt16Serializer : StructSerializerBase, IRepresentationConfigurable, IRepresentationConverterConfigurable { + #region static + private static readonly UInt16Serializer __instance = new(); + + /// + /// Gets a cached instance of an UInt16Serializer; + /// + public static UInt16Serializer Instance => __instance; + #endregion + // private fields private readonly BsonType _representation; private readonly RepresentationConverter _converter; diff --git a/src/MongoDB.Bson/Serialization/Serializers/UInt64Serializer.cs b/src/MongoDB.Bson/Serialization/Serializers/UInt64Serializer.cs index 88bfe7a101d..693b8f6cbf3 100644 --- a/src/MongoDB.Bson/Serialization/Serializers/UInt64Serializer.cs +++ b/src/MongoDB.Bson/Serialization/Serializers/UInt64Serializer.cs @@ -25,6 +25,15 @@ namespace MongoDB.Bson.Serialization.Serializers [CLSCompliant(false)] public sealed class UInt64Serializer : StructSerializerBase, IRepresentationConfigurable, IRepresentationConverterConfigurable { + #region static + private static readonly UInt64Serializer __instance = new(); + + /// + /// Gets a cached instance of an UInt64Serializer; + /// + public static UInt64Serializer Instance => __instance; + #endregion + // private fields private readonly BsonType _representation; private readonly RepresentationConverter _converter; diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Misc/SerializationHelper.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Misc/SerializationHelper.cs index a0377fdea76..1ef6b455531 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Misc/SerializationHelper.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Misc/SerializationHelper.cs @@ -239,5 +239,10 @@ public static BsonArray SerializeValues(IBsonSerializer itemSerializer, IEnumera } return document["_v"].AsBsonArray; } + + public static void EnsureSerializerIsCompatible(Expression memberExpression, IBsonSerializer serializer, IBsonSerializer expectedSerializer) + { + throw new System.NotImplementedException(); + } } } diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs index 4eb7b2a7661..9459ead301b 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs @@ -17,8 +17,10 @@ using System.Linq.Expressions; using MongoDB.Bson; using MongoDB.Bson.Serialization; +using MongoDB.Bson.Serialization.Serializers; using MongoDB.Driver.Linq.Linq3Implementation.Ast; using MongoDB.Driver.Linq.Linq3Implementation.Ast.Expressions; +using MongoDB.Driver.Linq.Linq3Implementation.ExtensionMethods; using MongoDB.Driver.Linq.Linq3Implementation.Misc; using MongoDB.Driver.Linq.Linq3Implementation.Reflection; @@ -36,135 +38,139 @@ public static TranslatedExpression Translate(TranslationContext context, MethodC throw new ExpressionNotSupportedException(expression); } - var fieldAst = ExpressionToAggregationExpressionTranslator.Translate(context, arguments[0]).Ast; - - ByteOrder? byteOrder; - string format; - AstExpression onErrorAst; - AstExpression onNullAst; - BsonBinarySubType? subType; - - var optionExpression = arguments[1]; - switch (optionExpression) - { - case ConstantExpression constantExpression: - ExtractOptionsFromConstantExpression(constantExpression, out byteOrder, out format, out onErrorAst, out onNullAst, out subType); - break; - case MemberInitExpression memberInitExpression: - ExtractOptionsFromMemberInitExpression(memberInitExpression, context, out byteOrder, out format, out onErrorAst, out onNullAst, out subType); - break; - default: - throw new ExpressionNotSupportedException("The 'Options' argument can be either a constant expression or a member initialization expression"); - } - var toType = method.GetGenericArguments()[1]; - var toBsonType = GetBsonType(toType).Render(); - var serializer = BsonSerializer.LookupSerializer(toType); + var valueExpression = arguments[0]; + var optionsExpression = arguments[1]; + + var (toBsonType, toSerializer) = TranslateToType(expression, toType); + var valueTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, valueExpression); + var (subType, byteOrder, format, onErrorAst, onNullAst) = TranslateOptions(context, expression, optionsExpression, toSerializer); - var ast = AstExpression.Convert(fieldAst, toBsonType, subType: subType, byteOrder: byteOrder, format: format, onError: onErrorAst, onNull: onNullAst); - return new TranslatedExpression(expression, ast, serializer); + var ast = AstExpression.Convert(valueTranslation.Ast, toBsonType.Render(), subType, byteOrder, format, onErrorAst, onNullAst); + return new TranslatedExpression(expression, ast, toSerializer); } - private static void ExtractOptionsFromConstantExpression(ConstantExpression constantExpression, out ByteOrder? byteOrder, out string format, out AstExpression onErrorAst, out AstExpression onNullAst, out BsonBinarySubType? subType) + private static (BsonBinarySubType? subType, ByteOrder? byteOrder, string format, AstExpression onErrorAst, AstExpression onNullAst) + TranslateOptions( + TranslationContext context, + Expression expression, + Expression optionsExpression, + IBsonSerializer toSerializer) { - byteOrder = null; - format = null; - onErrorAst = null; - onNullAst = null; - subType = null; + return optionsExpression switch + { + ConstantExpression constantExpression => TranslateOptions(constantExpression, toSerializer), + MemberInitExpression memberInitExpressionExpression => TranslateOptions(context, expression, memberInitExpressionExpression, toSerializer), + _ => throw new ExpressionNotSupportedException(optionsExpression, containingExpression: expression, because: "the Options argument must be either a constant or a member initialization expression.") + }; + } - var options = (ConvertOptions)constantExpression.Value; - if (options is null) - { - return; - } + private static (BsonBinarySubType? subType, ByteOrder? byteOrder, string format, AstExpression onErrorAst, AstExpression onNullAst) + TranslateOptions( + ConstantExpression optionsExpression, + IBsonSerializer toSerializer) + { + var options = (ConvertOptions)optionsExpression.Value; - if (options.OnErrorWasSet) + AstExpression onErrorAst = null; + AstExpression onNullAst = null; + if (options != null) { - onErrorAst = options.GetOnError(); - } + if (options.OnErrorWasSet(out var onErrorValue)) + { + var serializedOnErrorValue = SerializationHelper.SerializeValue(toSerializer, onErrorValue); + onErrorAst = AstExpression.Constant(serializedOnErrorValue); + } - if (options.OnNullWasSet) - { - onNullAst = options.GetOnNull(); + if (options.OnNullWasSet(out var onNullValue)) + { + var serializedOnNullValue = SerializationHelper.SerializeValue(toSerializer, onNullValue); + onNullAst = AstExpression.Constant(serializedOnNullValue); + } } - subType = options.SubType; - format = options.Format; - byteOrder = options.ByteOrder; + return (options?.SubType, options?.ByteOrder, options?.Format, onErrorAst, onNullAst); } - private static void ExtractOptionsFromMemberInitExpression(MemberInitExpression memberInitExpression, TranslationContext context, out ByteOrder? byteOrder, out string format, out AstExpression onErrorAst, out AstExpression onNullAst, out BsonBinarySubType? subType) + private static (BsonBinarySubType? subType, ByteOrder? byteOrder, string format, AstExpression onErrorAst, AstExpression onNullAst) + TranslateOptions( + TranslationContext context, + Expression expression, + MemberInitExpression optionsExpression, + IBsonSerializer toSerializer + ) { - byteOrder = null; - format = null; - onErrorAst = null; - onNullAst = null; - subType = null; + BsonBinarySubType? subType = null; + ByteOrder? byteOrder = null; + string format = null; + TranslatedExpression onErrorTranslation = null; + TranslatedExpression onNullTranslation = null; - foreach (var binding in memberInitExpression.Bindings) + foreach (var binding in optionsExpression.Bindings) { - if (binding is not MemberAssignment memberAssignment) continue; + if (binding is not MemberAssignment memberAssignment) + { + throw new ExpressionNotSupportedException(optionsExpression, containingExpression: expression, because: "only member assignment is supported"); + } var memberName = memberAssignment.Member.Name; - var expression = memberAssignment.Expression; + var memberExpression = memberAssignment.Expression; switch (memberName) { case nameof(ConvertOptions.ByteOrder): - byteOrder = GetConstantValue(expression, nameof(ConvertOptions.ByteOrder)); + byteOrder = memberExpression.GetConstantValue(expression); break; case nameof(ConvertOptions.Format): - format = GetConstantValue(expression, nameof(ConvertOptions.Format)); + format = memberExpression.GetConstantValue(expression); break; case nameof(ConvertOptions.OnError): - onErrorAst = ExpressionToAggregationExpressionTranslator.Translate(context, expression).Ast; + onErrorTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, memberExpression); + SerializationHelper.EnsureSerializerIsCompatible(memberExpression, onErrorTranslation.Serializer, expectedSerializer: toSerializer); break; case nameof(ConvertOptions.OnNull): - onNullAst = ExpressionToAggregationExpressionTranslator.Translate(context, expression).Ast; + onNullTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, memberExpression); + SerializationHelper.EnsureSerializerIsCompatible(memberExpression, onNullTranslation.Serializer, expectedSerializer: toSerializer); break; case nameof(ConvertOptions.SubType): - subType = GetConstantValue(expression, nameof(ConvertOptions.SubType)); + subType = memberExpression.GetConstantValue(expression); break; + default: + throw new ExpressionNotSupportedException(memberExpression, because: $"memberName {memberName} is invalid"); } } - } - private static T GetConstantValue(Expression expression, string fieldName) - { - if (expression is not ConstantExpression constantExpression) - { - throw new ExpressionNotSupportedException($"The {fieldName} field must be a constant expression."); - } - return (T)constantExpression.Value; + return (subType, byteOrder, format, onErrorTranslation?.Ast, onNullTranslation?.Ast); } - private static BsonType GetBsonType(Type type) + private static (BsonType ToBsonType, IBsonSerializer ToSerialzier) TranslateToType(Expression expression, Type toType) { - return Type.GetTypeCode(Nullable.GetUnderlyingType(type) ?? type) switch + return Type.GetTypeCode(Nullable.GetUnderlyingType(toType) ?? toType) switch { - TypeCode.Boolean => BsonType.Boolean, - TypeCode.Byte => BsonType.Int32, - TypeCode.SByte => BsonType.Int32, - TypeCode.Int16 => BsonType.Int32, - TypeCode.UInt16 => BsonType.Int32, - TypeCode.Int32 => BsonType.Int32, - TypeCode.UInt32 => BsonType.Int64, - TypeCode.Int64 => BsonType.Int64, - TypeCode.UInt64 => BsonType.Decimal128, - TypeCode.Single => BsonType.Double, - TypeCode.Double => BsonType.Double, - TypeCode.Decimal => BsonType.Decimal128, - TypeCode.String => BsonType.String, - TypeCode.Char => BsonType.String, - TypeCode.DateTime => BsonType.DateTime, - TypeCode.Object when type == typeof(byte[]) => BsonType.Binary, - TypeCode.Object when type == typeof(BsonBinaryData) => BsonType.Binary, - _ when type == typeof(Decimal128) => BsonType.Decimal128, - _ when type == typeof(Guid) => BsonType.Binary, - _ when type == typeof(ObjectId) => BsonType.ObjectId, - _ => BsonType.Document + TypeCode.Boolean => (BsonType.Boolean, BooleanSerializer.Instance), + TypeCode.Byte => (BsonType.Int32, ByteSerializer.Instance), + TypeCode.Char => (BsonType.String, StringSerializer.Instance), + TypeCode.DateTime => (BsonType.DateTime, DateTimeSerializer.Instance), + TypeCode.Decimal => (BsonType.Decimal128, DecimalSerializer.Instance), + TypeCode.Double => (BsonType.Double, DoubleSerializer.Instance), + TypeCode.Int16 => (BsonType.Int32, Int16Serializer.Instance), + TypeCode.Int32 => (BsonType.Int32, Int32Serializer.Instance), + TypeCode.Int64 => (BsonType.Int64, Int64Serializer.Instance), + TypeCode.SByte => (BsonType.Int32, SByteSerializer.Instance), + TypeCode.Single => (BsonType.Double, DoubleSerializer.Instance), + TypeCode.String => (BsonType.String, StringSerializer.Instance), + TypeCode.UInt16 => (BsonType.Int32, UInt16Serializer.Instance), + TypeCode.UInt32 => (BsonType.Int64, Int32Serializer.Instance), + TypeCode.UInt64 => (BsonType.Decimal128, UInt64Serializer.Instance), + + _ when toType == typeof(byte[]) => (BsonType.Binary, ByteArraySerializer.Instance), + _ when toType == typeof(BsonBinaryData) => (BsonType.Binary, BsonBinaryDataSerializer.Instance), + _ when toType == typeof(Decimal128) => (BsonType.Decimal128, Decimal128Serializer.Instance), + _ when toType == typeof(Guid) => (BsonType.Binary, GuidSerializer.StandardInstance), + _ when toType == typeof(ObjectId) => (BsonType.ObjectId, ObjectIdSerializer.Instance), + + _ => throw new ExpressionNotSupportedException(expression, because: $"{toType} is not a valid TTo for Convert") }; } } From d844cd28fb6f39975d1ec064931b7ba156413db5 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Mon, 7 Apr 2025 11:08:02 +0200 Subject: [PATCH 75/82] Corrections --- .../Misc/SerializationHelper.cs | 16 ++++--- ...MethodToAggregationExpressionTranslator.cs | 46 ++++++++++--------- ...dToAggregationExpressionTranslatorTests.cs | 2 +- 3 files changed, 34 insertions(+), 30 deletions(-) diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Misc/SerializationHelper.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Misc/SerializationHelper.cs index 1ef6b455531..86b28247f0a 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Misc/SerializationHelper.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Misc/SerializationHelper.cs @@ -77,6 +77,14 @@ public static void EnsureRepresentationIsNumeric(Expression expression, Expressi } } + public static void EnsureSerializerIsCompatible(Expression expression, IBsonSerializer actualSerializer, IBsonSerializer expectedSerializer) + { + if (!actualSerializer.Equals(expectedSerializer)) + { + throw new ExpressionNotSupportedException(expression, because: "the result serializer is not compatible with the expected serializer"); + } + } + public static BsonType GetRepresentation(IBsonSerializer serializer) { if (serializer is IDiscriminatedInterfaceSerializer discriminatedInterfaceSerializer) @@ -238,11 +246,5 @@ public static BsonArray SerializeValues(IBsonSerializer itemSerializer, IEnumera writer.WriteEndDocument(); } return document["_v"].AsBsonArray; - } - - public static void EnsureSerializerIsCompatible(Expression memberExpression, IBsonSerializer serializer, IBsonSerializer expectedSerializer) - { - throw new System.NotImplementedException(); - } - } + } } } diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs index 9459ead301b..9e26de0018f 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs @@ -146,32 +146,34 @@ IBsonSerializer toSerializer private static (BsonType ToBsonType, IBsonSerializer ToSerialzier) TranslateToType(Expression expression, Type toType) { - return Type.GetTypeCode(Nullable.GetUnderlyingType(toType) ?? toType) switch + var bsonType = Type.GetTypeCode(Nullable.GetUnderlyingType(toType) ?? toType) switch { - TypeCode.Boolean => (BsonType.Boolean, BooleanSerializer.Instance), - TypeCode.Byte => (BsonType.Int32, ByteSerializer.Instance), - TypeCode.Char => (BsonType.String, StringSerializer.Instance), - TypeCode.DateTime => (BsonType.DateTime, DateTimeSerializer.Instance), - TypeCode.Decimal => (BsonType.Decimal128, DecimalSerializer.Instance), - TypeCode.Double => (BsonType.Double, DoubleSerializer.Instance), - TypeCode.Int16 => (BsonType.Int32, Int16Serializer.Instance), - TypeCode.Int32 => (BsonType.Int32, Int32Serializer.Instance), - TypeCode.Int64 => (BsonType.Int64, Int64Serializer.Instance), - TypeCode.SByte => (BsonType.Int32, SByteSerializer.Instance), - TypeCode.Single => (BsonType.Double, DoubleSerializer.Instance), - TypeCode.String => (BsonType.String, StringSerializer.Instance), - TypeCode.UInt16 => (BsonType.Int32, UInt16Serializer.Instance), - TypeCode.UInt32 => (BsonType.Int64, Int32Serializer.Instance), - TypeCode.UInt64 => (BsonType.Decimal128, UInt64Serializer.Instance), - - _ when toType == typeof(byte[]) => (BsonType.Binary, ByteArraySerializer.Instance), - _ when toType == typeof(BsonBinaryData) => (BsonType.Binary, BsonBinaryDataSerializer.Instance), - _ when toType == typeof(Decimal128) => (BsonType.Decimal128, Decimal128Serializer.Instance), - _ when toType == typeof(Guid) => (BsonType.Binary, GuidSerializer.StandardInstance), - _ when toType == typeof(ObjectId) => (BsonType.ObjectId, ObjectIdSerializer.Instance), + TypeCode.Boolean => BsonType.Boolean, + TypeCode.Byte => BsonType.Int32, + TypeCode.Char => (BsonType.String), + TypeCode.DateTime => (BsonType.DateTime), + TypeCode.Decimal => (BsonType.Decimal128), + TypeCode.Double => (BsonType.Double), + TypeCode.Int16 => (BsonType.Int32), + TypeCode.Int32 => (BsonType.Int32), + TypeCode.Int64 => (BsonType.Int64), + TypeCode.SByte => (BsonType.Int32), + TypeCode.Single => (BsonType.Double), + TypeCode.String => (BsonType.String), + TypeCode.UInt16 => (BsonType.Int32), + TypeCode.UInt32 => (BsonType.Int64), + TypeCode.UInt64 => (BsonType.Decimal128), + + _ when toType == typeof(byte[]) => (BsonType.Binary), + _ when toType == typeof(BsonBinaryData) => (BsonType.Binary), + _ when toType == typeof(Decimal128) => (BsonType.Decimal128), + _ when toType == typeof(Guid) => (BsonType.Binary), + _ when toType == typeof(ObjectId) => (BsonType.ObjectId), _ => throw new ExpressionNotSupportedException(expression, because: $"{toType} is not a valid TTo for Convert") }; + + return (bsonType, BsonSerializer.LookupSerializer(toType)); } } } diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs index a549849f739..bbaa0a30c06 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs @@ -463,7 +463,7 @@ public void Convert_to_nullable_value_type_should_work(int id, int? expectedResu // To binData new object[] { 4, (Expression>)(x => Mql.Convert(x.IntProperty, new ConvertOptions { SubType = BsonBinarySubType.Binary, ByteOrder = ByteOrder.LittleEndian })), - "{ $project: { _v : { $convert : { input : '$IntProperty', to : { type: 'binData', subtype: 0 }, byteOrder : 'little' }, _id : 0 } } }", + "{ $project: { _v : { $convert : { input : '$IntProperty', to : { type: 'binData', subtype: 0 }, byteOrder : 'little' } }, _id : 0 } }", new BsonBinaryData(Convert.FromBase64String("ogIAAA==")) }, }; From ec52d7f74241c6c7a9451782c34ffa60b9906bba Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Mon, 7 Apr 2025 12:47:40 +0200 Subject: [PATCH 76/82] Small corrections --- ...MethodToAggregationExpressionTranslator.cs | 62 +++++++++++++------ ...dToAggregationExpressionTranslatorTests.cs | 11 +++- 2 files changed, 51 insertions(+), 22 deletions(-) diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs index 9e26de0018f..20475797a27 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs @@ -23,6 +23,7 @@ using MongoDB.Driver.Linq.Linq3Implementation.ExtensionMethods; using MongoDB.Driver.Linq.Linq3Implementation.Misc; using MongoDB.Driver.Linq.Linq3Implementation.Reflection; +using MongoDB.Driver.Linq.Linq3Implementation.Serializers; namespace MongoDB.Driver.Linq.Linq3Implementation.Translators.ExpressionToAggregationExpressionTranslators.MethodTranslators { @@ -146,34 +147,55 @@ IBsonSerializer toSerializer private static (BsonType ToBsonType, IBsonSerializer ToSerialzier) TranslateToType(Expression expression, Type toType) { + if (toType == typeof(byte[])) + { + return (BsonType.Binary, ByteArraySerializer.Instance); + } + if (toType == typeof(BsonBinaryData)) + { + return (BsonType.Binary, BsonBinaryDataSerializer.Instance); + } + if (toType == typeof(Decimal128)) + { + return (BsonType.Decimal128, Decimal128Serializer.Instance); + } + if (toType == typeof(Guid)) + { + return (BsonType.Binary, GuidSerializer.StandardInstance); + } + if (toType == typeof(ObjectId)) + { + return (BsonType.ObjectId, ObjectIdSerializer.Instance); + } + if (toType == typeof(string)) + { + return (BsonType.String, StringSerializer.Instance); + } + if (toType == typeof(DateTime)) + { + return (BsonType.DateTime, DateTimeSerializer.Instance); + } + var bsonType = Type.GetTypeCode(Nullable.GetUnderlyingType(toType) ?? toType) switch { TypeCode.Boolean => BsonType.Boolean, TypeCode.Byte => BsonType.Int32, - TypeCode.Char => (BsonType.String), - TypeCode.DateTime => (BsonType.DateTime), - TypeCode.Decimal => (BsonType.Decimal128), - TypeCode.Double => (BsonType.Double), - TypeCode.Int16 => (BsonType.Int32), - TypeCode.Int32 => (BsonType.Int32), - TypeCode.Int64 => (BsonType.Int64), - TypeCode.SByte => (BsonType.Int32), - TypeCode.Single => (BsonType.Double), - TypeCode.String => (BsonType.String), - TypeCode.UInt16 => (BsonType.Int32), - TypeCode.UInt32 => (BsonType.Int64), - TypeCode.UInt64 => (BsonType.Decimal128), - - _ when toType == typeof(byte[]) => (BsonType.Binary), - _ when toType == typeof(BsonBinaryData) => (BsonType.Binary), - _ when toType == typeof(Decimal128) => (BsonType.Decimal128), - _ when toType == typeof(Guid) => (BsonType.Binary), - _ when toType == typeof(ObjectId) => (BsonType.ObjectId), + TypeCode.Char => BsonType.String, + TypeCode.Decimal => BsonType.Decimal128, + TypeCode.Double => BsonType.Double, + TypeCode.Int16 => BsonType.Int32, + TypeCode.Int32 => BsonType.Int32, + TypeCode.Int64 => BsonType.Int64, + TypeCode.SByte => BsonType.Int32, + TypeCode.Single => BsonType.Double, + TypeCode.UInt16 => BsonType.Int32, + TypeCode.UInt32 => BsonType.Int64, + TypeCode.UInt64 => BsonType.Decimal128, _ => throw new ExpressionNotSupportedException(expression, because: $"{toType} is not a valid TTo for Convert") }; - return (bsonType, BsonSerializer.LookupSerializer(toType)); + return (bsonType, StandardSerializers.GetSerializer(toType)); } } } diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs index bbaa0a30c06..2e9904753d6 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs @@ -438,10 +438,12 @@ public void Convert_to_nullable_value_type_should_work(int id, int? expectedResu "{ $project: { _v : { $toDecimal : '$IntProperty' }, _id : 0 } }", new Decimal128(33) }, - // To float/double + // To float new object[] { 22, (Expression>)(x => Mql.Convert(x.IntProperty, null)), "{ $project: { _v : { $toDouble : '$IntProperty' }, _id : 0 } }", (float)33.0 }, + + // To double new object[] { 22, (Expression>)(x => Mql.Convert(x.IntProperty, null)), "{ $project: { _v : { $toDouble : '$IntProperty' }, _id : 0 } }", 33.0 }, @@ -461,10 +463,15 @@ public void Convert_to_nullable_value_type_should_work(int id, int? expectedResu "{ $project: { _v : { $toString : '$IntProperty' }, _id : 0 } }", "33" }, - // To binData + // To BsonBinaryData new object[] { 4, (Expression>)(x => Mql.Convert(x.IntProperty, new ConvertOptions { SubType = BsonBinarySubType.Binary, ByteOrder = ByteOrder.LittleEndian })), "{ $project: { _v : { $convert : { input : '$IntProperty', to : { type: 'binData', subtype: 0 }, byteOrder : 'little' } }, _id : 0 } }", new BsonBinaryData(Convert.FromBase64String("ogIAAA==")) }, + + // To byte[] + new object[] { 4, (Expression>)(x => Mql.Convert(x.IntProperty, new ConvertOptions { SubType = BsonBinarySubType.Binary, ByteOrder = ByteOrder.LittleEndian })), + "{ $project: { _v : { $convert : { input : '$IntProperty', to : { type: 'binData', subtype: 0 }, byteOrder : 'little' } }, _id : 0 } }", + Convert.FromBase64String("ogIAAA==") }, }; [Theory] From f8a5a66809fb2389570e33830764d891419788ca Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Tue, 8 Apr 2025 09:33:11 +0200 Subject: [PATCH 77/82] Added test --- ...thodToAggregationExpressionTranslatorTests.cs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs index 2e9904753d6..336b1f8496d 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs @@ -17,9 +17,11 @@ using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; +using FluentAssertions; using MongoDB.Bson; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.TestHelpers.XunitExtensions; +using MongoDB.Driver.Linq; using MongoDB.Driver.TestHelpers; using Xunit; @@ -492,6 +494,20 @@ public void Convert_should_work( AssertOutcome(collection, queryable, expectedStages, expectedValue); } + [Fact] + public void Convert_should_throw_when_using_unrecognized_to_type() + { + var collection = Fixture.Collection; + var queryable = collection.AsQueryable() + .Where(x => x.Id == 20) + .Select(x => Mql.Convert("123", new ConvertOptions())); + + var exception = Record.Exception(() => Translate(collection, queryable)); + + Assert.NotNull(exception); + Assert.IsType(exception); + } + private void AssertOutcome(IMongoCollection collection, IQueryable queryable, string[] expectedStages, From abda35393fc302b6f9a0e7dd4b68247bb2b302f3 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Tue, 8 Apr 2025 09:33:20 +0200 Subject: [PATCH 78/82] Removed unused --- src/MongoDB.Driver/ConvertOptions.cs | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/MongoDB.Driver/ConvertOptions.cs b/src/MongoDB.Driver/ConvertOptions.cs index 75a5f0b1a72..e796b0a2ecb 100644 --- a/src/MongoDB.Driver/ConvertOptions.cs +++ b/src/MongoDB.Driver/ConvertOptions.cs @@ -14,8 +14,6 @@ */ using MongoDB.Bson; -using MongoDB.Bson.Serialization; -using MongoDB.Driver.Linq.Linq3Implementation.Serializers; namespace MongoDB.Driver { @@ -71,17 +69,6 @@ public class ConvertOptions : ConvertOptions private bool _onErrorWasSet; private TTo _onNull; private bool _onNullWasSet; - private readonly IBsonSerializer _serializer; - - /// - /// Initializes a new instance of the class. - /// - public ConvertOptions() - { - _serializer = StandardSerializers.TryGetSerializer(typeof(TTo), out var serializer) - ? serializer - : BsonSerializer.LookupSerializer(typeof(TTo)); - } /// /// The onError parameter. From e2f2f6a467076c6d4b0ea2a713470812e85dcb61 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Tue, 8 Apr 2025 09:36:31 +0200 Subject: [PATCH 79/82] Restored files --- .../MemberExpressionToAggregationExpressionTranslator.cs | 3 ++- ...teFromStringMethodToAggregationExpressionTranslatorTests.cs | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MemberExpressionToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MemberExpressionToAggregationExpressionTranslator.cs index 3ff7d0c6b5a..58cf7f6ab61 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MemberExpressionToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MemberExpressionToAggregationExpressionTranslator.cs @@ -43,6 +43,7 @@ public static TranslatedExpression Translate(TranslationContext context, MemberE { case "HasValue": return HasValuePropertyToAggregationExpressionTranslator.Translate(context, expression); case "Value": return ValuePropertyToAggregationExpressionTranslator.Translate(context, expression); + default: break; } } @@ -65,7 +66,7 @@ public static TranslatedExpression Translate(TranslationContext context, MemberE if (!DocumentSerializerHelper.AreMembersRepresentedAsFields(containerTranslation.Serializer, out _)) { - if (member is PropertyInfo propertyInfo && propertyInfo.Name == "Length") + if (member is PropertyInfo propertyInfo && propertyInfo.Name == "Length") { return LengthPropertyToAggregationExpressionTranslator.Translate(context, expression); } diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/DateFromStringMethodToAggregationExpressionTranslatorTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/DateFromStringMethodToAggregationExpressionTranslatorTests.cs index e774a7b0b80..69dae706333 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/DateFromStringMethodToAggregationExpressionTranslatorTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/DateFromStringMethodToAggregationExpressionTranslatorTests.cs @@ -21,6 +21,7 @@ using MongoDB.Bson; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.TestHelpers.XunitExtensions; +using MongoDB.Driver.Linq; using Xunit; namespace MongoDB.Driver.Tests.Linq.Linq3Implementation.Translators.ExpressionToAggregationExpressionTranslators.MethodTranslators From afa7bdd92617dbc2c71ae94d2220e95a88e32be5 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Tue, 8 Apr 2025 10:11:21 +0200 Subject: [PATCH 80/82] Small fixes --- .../Serializers/SingleSerializer.cs | 9 +++ src/MongoDB.Driver/ConvertOptions.cs | 2 +- .../Misc/SerializationHelper.cs | 7 +- ...MethodToAggregationExpressionTranslator.cs | 80 +++++++------------ ...dToAggregationExpressionTranslatorTests.cs | 2 + 5 files changed, 47 insertions(+), 53 deletions(-) diff --git a/src/MongoDB.Bson/Serialization/Serializers/SingleSerializer.cs b/src/MongoDB.Bson/Serialization/Serializers/SingleSerializer.cs index 24301520235..9d3e691ebac 100644 --- a/src/MongoDB.Bson/Serialization/Serializers/SingleSerializer.cs +++ b/src/MongoDB.Bson/Serialization/Serializers/SingleSerializer.cs @@ -24,6 +24,15 @@ namespace MongoDB.Bson.Serialization.Serializers /// public sealed class SingleSerializer : StructSerializerBase, IRepresentationConfigurable, IRepresentationConverterConfigurable { + #region static + private static readonly SingleSerializer __instance = new(); + + /// + /// Gets a cached instance of an SingleSerializer; + /// + public static SingleSerializer Instance => __instance; + #endregion + // private fields private readonly BsonType _representation; private readonly RepresentationConverter _converter; diff --git a/src/MongoDB.Driver/ConvertOptions.cs b/src/MongoDB.Driver/ConvertOptions.cs index e796b0a2ecb..bb870406106 100644 --- a/src/MongoDB.Driver/ConvertOptions.cs +++ b/src/MongoDB.Driver/ConvertOptions.cs @@ -55,7 +55,7 @@ public BsonBinarySubType? SubType internal abstract bool OnErrorWasSet(out object onError); - internal abstract bool OnNullWasSet(out object onError); + internal abstract bool OnNullWasSet(out object onNull); } /// diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Misc/SerializationHelper.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Misc/SerializationHelper.cs index 86b28247f0a..0b34b0bd7cf 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Misc/SerializationHelper.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Misc/SerializationHelper.cs @@ -77,11 +77,11 @@ public static void EnsureRepresentationIsNumeric(Expression expression, Expressi } } - public static void EnsureSerializerIsCompatible(Expression expression, IBsonSerializer actualSerializer, IBsonSerializer expectedSerializer) + public static void EnsureSerializerIsCompatible(Expression expression, Expression containingExpression, IBsonSerializer actualSerializer, IBsonSerializer expectedSerializer) { if (!actualSerializer.Equals(expectedSerializer)) { - throw new ExpressionNotSupportedException(expression, because: "the result serializer is not compatible with the expected serializer"); + throw new ExpressionNotSupportedException(expression, containingExpression, because: "the result serializer is not compatible with the expected serializer"); } } @@ -246,5 +246,6 @@ public static BsonArray SerializeValues(IBsonSerializer itemSerializer, IEnumera writer.WriteEndDocument(); } return document["_v"].AsBsonArray; - } } + } + } } diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs index 20475797a27..f966e1f205b 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs @@ -23,7 +23,6 @@ using MongoDB.Driver.Linq.Linq3Implementation.ExtensionMethods; using MongoDB.Driver.Linq.Linq3Implementation.Misc; using MongoDB.Driver.Linq.Linq3Implementation.Reflection; -using MongoDB.Driver.Linq.Linq3Implementation.Serializers; namespace MongoDB.Driver.Linq.Linq3Implementation.Translators.ExpressionToAggregationExpressionTranslators.MethodTranslators { @@ -62,11 +61,10 @@ private static (BsonBinarySubType? subType, ByteOrder? byteOrder, string format, { ConstantExpression constantExpression => TranslateOptions(constantExpression, toSerializer), MemberInitExpression memberInitExpressionExpression => TranslateOptions(context, expression, memberInitExpressionExpression, toSerializer), - _ => throw new ExpressionNotSupportedException(optionsExpression, containingExpression: expression, because: "the Options argument must be either a constant or a member initialization expression.") + _ => throw new ExpressionNotSupportedException(optionsExpression, containingExpression: expression, because: "the options argument must be either a constant or a member initialization expression.") }; } - private static (BsonBinarySubType? subType, ByteOrder? byteOrder, string format, AstExpression onErrorAst, AstExpression onNullAst) TranslateOptions( ConstantExpression optionsExpression, @@ -128,11 +126,11 @@ IBsonSerializer toSerializer break; case nameof(ConvertOptions.OnError): onErrorTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, memberExpression); - SerializationHelper.EnsureSerializerIsCompatible(memberExpression, onErrorTranslation.Serializer, expectedSerializer: toSerializer); + SerializationHelper.EnsureSerializerIsCompatible(memberExpression, containingExpression: expression, onErrorTranslation.Serializer, expectedSerializer: toSerializer); break; case nameof(ConvertOptions.OnNull): onNullTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, memberExpression); - SerializationHelper.EnsureSerializerIsCompatible(memberExpression, onNullTranslation.Serializer, expectedSerializer: toSerializer); + SerializationHelper.EnsureSerializerIsCompatible(memberExpression, containingExpression: expression, onNullTranslation.Serializer, expectedSerializer: toSerializer); break; case nameof(ConvertOptions.SubType): subType = memberExpression.GetConstantValue(expression); @@ -145,57 +143,41 @@ IBsonSerializer toSerializer return (subType, byteOrder, format, onErrorTranslation?.Ast, onNullTranslation?.Ast); } - private static (BsonType ToBsonType, IBsonSerializer ToSerialzier) TranslateToType(Expression expression, Type toType) + private static (BsonType ToBsonType, IBsonSerializer ToSerializer) TranslateToType(Expression expression, Type toType) { - if (toType == typeof(byte[])) - { - return (BsonType.Binary, ByteArraySerializer.Instance); - } - if (toType == typeof(BsonBinaryData)) - { - return (BsonType.Binary, BsonBinaryDataSerializer.Instance); - } - if (toType == typeof(Decimal128)) - { - return (BsonType.Decimal128, Decimal128Serializer.Instance); - } - if (toType == typeof(Guid)) - { - return (BsonType.Binary, GuidSerializer.StandardInstance); - } - if (toType == typeof(ObjectId)) - { - return (BsonType.ObjectId, ObjectIdSerializer.Instance); - } - if (toType == typeof(string)) - { - return (BsonType.String, StringSerializer.Instance); - } - if (toType == typeof(DateTime)) - { - return (BsonType.DateTime, DateTimeSerializer.Instance); - } + var underlyingType = Nullable.GetUnderlyingType(toType); + var isNullable = underlyingType != null; + var effectiveType = underlyingType ?? toType; - var bsonType = Type.GetTypeCode(Nullable.GetUnderlyingType(toType) ?? toType) switch + (BsonType, IBsonSerializer) result = Type.GetTypeCode(effectiveType) switch { - TypeCode.Boolean => BsonType.Boolean, - TypeCode.Byte => BsonType.Int32, - TypeCode.Char => BsonType.String, - TypeCode.Decimal => BsonType.Decimal128, - TypeCode.Double => BsonType.Double, - TypeCode.Int16 => BsonType.Int32, - TypeCode.Int32 => BsonType.Int32, - TypeCode.Int64 => BsonType.Int64, - TypeCode.SByte => BsonType.Int32, - TypeCode.Single => BsonType.Double, - TypeCode.UInt16 => BsonType.Int32, - TypeCode.UInt32 => BsonType.Int64, - TypeCode.UInt64 => BsonType.Decimal128, + TypeCode.Boolean => (BsonType.Boolean, BooleanSerializer.Instance), + TypeCode.Byte => (BsonType.Int32, ByteSerializer.Instance), + TypeCode.Char => (BsonType.String, StringSerializer.Instance), + TypeCode.DateTime => (BsonType.DateTime, DateTimeSerializer.Instance), + TypeCode.Decimal => (BsonType.Decimal128, DecimalSerializer.Instance), + TypeCode.Double => (BsonType.Double, DoubleSerializer.Instance), + TypeCode.Int16 => (BsonType.Int32, Int16Serializer.Instance), + TypeCode.Int32 => (BsonType.Int32, Int32Serializer.Instance), + TypeCode.Int64 => (BsonType.Int64, Int64Serializer.Instance), + TypeCode.SByte => (BsonType.Int32, SByteSerializer.Instance), + TypeCode.Single => (BsonType.Double, SingleSerializer.Instance), + TypeCode.String => (BsonType.String, StringSerializer.Instance), + TypeCode.UInt16 => (BsonType.Int32, UInt16Serializer.Instance), + TypeCode.UInt32 => (BsonType.Int64, Int32Serializer.Instance), + TypeCode.UInt64 => (BsonType.Decimal128, UInt64Serializer.Instance), + + _ when effectiveType == typeof(byte[]) => (BsonType.Binary, ByteArraySerializer.Instance), + _ when effectiveType == typeof(BsonBinaryData) => (BsonType.Binary, BsonBinaryDataSerializer.Instance), + _ when effectiveType == typeof(Decimal128) => (BsonType.Decimal128, Decimal128Serializer.Instance), + _ when effectiveType == typeof(Guid) => (BsonType.Binary, GuidSerializer.StandardInstance), + _ when effectiveType == typeof(ObjectId) => (BsonType.ObjectId, ObjectIdSerializer.Instance), _ => throw new ExpressionNotSupportedException(expression, because: $"{toType} is not a valid TTo for Convert") }; - return (bsonType, StandardSerializers.GetSerializer(toType)); + var (bsonType, serializer) = result; + return (bsonType, isNullable ? NullableSerializer.Create(serializer) : serializer); } } } diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs index 336b1f8496d..c689d491c9b 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs @@ -436,6 +436,8 @@ public void Convert_to_nullable_value_type_should_work(int id, int? expectedResu new object[] { 22, (Expression>)(x => Mql.Convert(x.IntProperty, null)), "{ $project: { _v : { $toDecimal : '$IntProperty' }, _id : 0 } }", 33m }, + + // To decimal128 new object[] { 22, (Expression>)(x => Mql.Convert(x.IntProperty, null)), "{ $project: { _v : { $toDecimal : '$IntProperty' }, _id : 0 } }", new Decimal128(33) }, From de05dd6667e34fabef1283e481eea418ccb8d6f2 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Thu, 10 Apr 2025 21:30:18 +0200 Subject: [PATCH 81/82] Small corrections + test fix --- src/MongoDB.Driver/ConvertOptions.cs | 8 +++--- ...essionToAggregationExpressionTranslator.cs | 1 + ...MethodToAggregationExpressionTranslator.cs | 26 +++++++++---------- ...dToAggregationExpressionTranslatorTests.cs | 15 +++++++---- 4 files changed, 27 insertions(+), 23 deletions(-) diff --git a/src/MongoDB.Driver/ConvertOptions.cs b/src/MongoDB.Driver/ConvertOptions.cs index bb870406106..8f70841fbe0 100644 --- a/src/MongoDB.Driver/ConvertOptions.cs +++ b/src/MongoDB.Driver/ConvertOptions.cs @@ -53,9 +53,9 @@ public BsonBinarySubType? SubType set => _subType = value; } - internal abstract bool OnErrorWasSet(out object onError); + internal abstract bool TryGetOnError(out object onError); - internal abstract bool OnNullWasSet(out object onNull); + internal abstract bool TryGetOnNull(out object onNull); } /// @@ -96,13 +96,13 @@ public TTo OnNull } } - internal override bool OnErrorWasSet(out object onError) + internal override bool TryGetOnError(out object onError) { onError = _onError; return _onErrorWasSet; } - internal override bool OnNullWasSet(out object onNull) + internal override bool TryGetOnNull(out object onNull) { onNull = _onNull; return _onNullWasSet; diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/ConstantExpressionToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/ConstantExpressionToAggregationExpressionTranslator.cs index 6b9ca5b0a8c..7487627213d 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/ConstantExpressionToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/ConstantExpressionToAggregationExpressionTranslator.cs @@ -14,6 +14,7 @@ */ using System.Linq.Expressions; +using MongoDB.Bson.IO; using MongoDB.Bson.Serialization; using MongoDB.Driver.Linq.Linq3Implementation.Ast.Expressions; using MongoDB.Driver.Linq.Linq3Implementation.Serializers; diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs index f966e1f205b..6bdff37fe1d 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs @@ -76,13 +76,13 @@ private static (BsonBinarySubType? subType, ByteOrder? byteOrder, string format, AstExpression onNullAst = null; if (options != null) { - if (options.OnErrorWasSet(out var onErrorValue)) + if (options.TryGetOnError(out var onErrorValue)) { var serializedOnErrorValue = SerializationHelper.SerializeValue(toSerializer, onErrorValue); onErrorAst = AstExpression.Constant(serializedOnErrorValue); } - if (options.OnNullWasSet(out var onNullValue)) + if (options.TryGetOnNull(out var onNullValue)) { var serializedOnNullValue = SerializationHelper.SerializeValue(toSerializer, onNullValue); onNullAst = AstExpression.Constant(serializedOnNullValue); @@ -145,11 +145,10 @@ IBsonSerializer toSerializer private static (BsonType ToBsonType, IBsonSerializer ToSerializer) TranslateToType(Expression expression, Type toType) { - var underlyingType = Nullable.GetUnderlyingType(toType); - var isNullable = underlyingType != null; - var effectiveType = underlyingType ?? toType; + var isNullable = toType.IsNullable(); + var valueType = isNullable ? Nullable.GetUnderlyingType(toType) : toType; - (BsonType, IBsonSerializer) result = Type.GetTypeCode(effectiveType) switch + var (bsonType, valueSerializer) = (ValueTuple)(Type.GetTypeCode(valueType) switch { TypeCode.Boolean => (BsonType.Boolean, BooleanSerializer.Instance), TypeCode.Byte => (BsonType.Int32, ByteSerializer.Instance), @@ -167,17 +166,16 @@ private static (BsonType ToBsonType, IBsonSerializer ToSerializer) TranslateToTy TypeCode.UInt32 => (BsonType.Int64, Int32Serializer.Instance), TypeCode.UInt64 => (BsonType.Decimal128, UInt64Serializer.Instance), - _ when effectiveType == typeof(byte[]) => (BsonType.Binary, ByteArraySerializer.Instance), - _ when effectiveType == typeof(BsonBinaryData) => (BsonType.Binary, BsonBinaryDataSerializer.Instance), - _ when effectiveType == typeof(Decimal128) => (BsonType.Decimal128, Decimal128Serializer.Instance), - _ when effectiveType == typeof(Guid) => (BsonType.Binary, GuidSerializer.StandardInstance), - _ when effectiveType == typeof(ObjectId) => (BsonType.ObjectId, ObjectIdSerializer.Instance), + _ when valueType == typeof(byte[]) => (BsonType.Binary, ByteArraySerializer.Instance), + _ when valueType == typeof(BsonBinaryData) => (BsonType.Binary, BsonBinaryDataSerializer.Instance), + _ when valueType == typeof(Decimal128) => (BsonType.Decimal128, Decimal128Serializer.Instance), + _ when valueType == typeof(Guid) => (BsonType.Binary, GuidSerializer.StandardInstance), + _ when valueType == typeof(ObjectId) => (BsonType.ObjectId, ObjectIdSerializer.Instance), _ => throw new ExpressionNotSupportedException(expression, because: $"{toType} is not a valid TTo for Convert") - }; + }); - var (bsonType, serializer) = result; - return (bsonType, isNullable ? NullableSerializer.Create(serializer) : serializer); + return (bsonType, isNullable ? NullableSerializer.Create(valueSerializer) : valueSerializer); } } } diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs index c689d491c9b..7759baf34dd 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslatorTests.cs @@ -41,7 +41,7 @@ public ConvertMethodToAggregationExpressionTranslatorTests(ClassFixture fixture) [InlineData(10, ByteOrder.BigEndian, null, "MongoCommandException")] public void Convert_to_BsonBinaryData_from_int_should_work(int id, ByteOrder byteOrder, string expectedBase64, string expectedException) { - RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromString); + RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); var collection = Fixture.Collection; var queryable = collection.AsQueryable() @@ -65,7 +65,7 @@ public void Convert_to_BsonBinaryData_from_int_should_work(int id, ByteOrder byt [InlineData(10, ByteOrder.BigEndian, null, "MongoCommandException")] public void Convert_to_BsonBinaryData_from_long_should_work(int id, ByteOrder byteOrder, string expectedBase64, string expectedException) { - RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromString); + RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); var collection = Fixture.Collection; var queryable = collection.AsQueryable() @@ -98,7 +98,7 @@ public void Convert_to_BsonBinaryData_from_long_should_work(int id, ByteOrder by [InlineData(10, ByteOrder.BigEndian, null, "MongoCommandException")] public void Convert_to_BsonBinaryData_from_double_should_work(int id, ByteOrder byteOrder, string expectedBase64, string expectedException) { - RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromString); + RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); var collection = Fixture.Collection; var queryable = collection.AsQueryable() @@ -295,7 +295,7 @@ public void Convert_with_format_should_be_rendered_correctly(string format, stri [InlineData(5, ByteOrder.BigEndian, "wAQAAAAAAAA=" )] public void Convert_with_byteOrder_should_be_rendered_correctly(int id, ByteOrder byteOrder, string expectedBase64) { - RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromString); + RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); var collection = Fixture.Collection; var queryable = collection.AsQueryable() @@ -319,7 +319,7 @@ public void Convert_with_byteOrder_should_be_rendered_correctly(int id, ByteOrde [InlineData(BsonBinarySubType.UserDefined, 128)] public void Convert_with_subtype_should_be_rendered_correctly(BsonBinarySubType subType, int expectedSubTypeValue) { - RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromString); + RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); const int id = 3; var collection = Fixture.Collection; @@ -486,6 +486,11 @@ public void Convert_should_work( string expectedStage, object expectedValue) { + if (expectedStage.Contains("byteOrder")) + { + RequireServer.Check().Supports(Feature.ConvertOperatorBinDataToFromNumeric); + } + var collection = Fixture.Collection; var queryable = collection.AsQueryable() .Where(x => x.Id == id) From 3b19233dc936ae8a9ad236d4fdd8945bdfb05fed Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Fri, 11 Apr 2025 09:56:52 +0200 Subject: [PATCH 82/82] Small naming correction --- src/MongoDB.Driver/ConvertOptions.cs | 8 ++++---- .../ConvertMethodToAggregationExpressionTranslator.cs | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/MongoDB.Driver/ConvertOptions.cs b/src/MongoDB.Driver/ConvertOptions.cs index 8f70841fbe0..bb870406106 100644 --- a/src/MongoDB.Driver/ConvertOptions.cs +++ b/src/MongoDB.Driver/ConvertOptions.cs @@ -53,9 +53,9 @@ public BsonBinarySubType? SubType set => _subType = value; } - internal abstract bool TryGetOnError(out object onError); + internal abstract bool OnErrorWasSet(out object onError); - internal abstract bool TryGetOnNull(out object onNull); + internal abstract bool OnNullWasSet(out object onNull); } /// @@ -96,13 +96,13 @@ public TTo OnNull } } - internal override bool TryGetOnError(out object onError) + internal override bool OnErrorWasSet(out object onError) { onError = _onError; return _onErrorWasSet; } - internal override bool TryGetOnNull(out object onNull) + internal override bool OnNullWasSet(out object onNull) { onNull = _onNull; return _onNullWasSet; diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs index 6bdff37fe1d..9f6844b3031 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConvertMethodToAggregationExpressionTranslator.cs @@ -76,13 +76,13 @@ private static (BsonBinarySubType? subType, ByteOrder? byteOrder, string format, AstExpression onNullAst = null; if (options != null) { - if (options.TryGetOnError(out var onErrorValue)) + if (options.OnErrorWasSet(out var onErrorValue)) { var serializedOnErrorValue = SerializationHelper.SerializeValue(toSerializer, onErrorValue); onErrorAst = AstExpression.Constant(serializedOnErrorValue); } - if (options.TryGetOnNull(out var onNullValue)) + if (options.OnNullWasSet(out var onNullValue)) { var serializedOnNullValue = SerializationHelper.SerializeValue(toSerializer, onNullValue); onNullAst = AstExpression.Constant(serializedOnNullValue);