diff --git a/src/MongoDB.Driver/Core/Misc/Feature.cs b/src/MongoDB.Driver/Core/Misc/Feature.cs index 720cf3c19db..afdc126f9c7 100644 --- a/src/MongoDB.Driver/Core/Misc/Feature.cs +++ b/src/MongoDB.Driver/Core/Misc/Feature.cs @@ -91,6 +91,7 @@ public class Feature private static readonly Feature __setWindowFields = new Feature("SetWindowFields", WireVersion.Server50); private static readonly Feature __setWindowFieldsLocf = new Feature("SetWindowFieldsLocf", WireVersion.Server52); private static readonly Feature __shardedTransactions = new Feature("ShardedTransactions", WireVersion.Server42); + private static readonly Feature __sigmoidOperator = new Feature("SigmoidOperator", WireVersion.Server81); private static readonly Feature __snapshotReads = new Feature("SnapshotReads", WireVersion.Server50, notSupportedMessage: "Snapshot reads require MongoDB 5.0 or later"); private static readonly Feature __sortArrayOperator = new Feature("SortArrayOperator", WireVersion.Server52); private static readonly Feature __speculativeAuthentication = new Feature("SpeculativeAuthentication", WireVersion.Server44); @@ -431,6 +432,11 @@ public class Feature /// public static Feature ShardedTransactions => __shardedTransactions; + /// + /// Gets the $sigmoid operator feature. + /// + public static Feature SigmoidOperator => __sigmoidOperator; + /// /// Gets the snapshot reads feature. /// diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstUnaryOperator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstUnaryOperator.cs index a712962ae69..17422736971 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstUnaryOperator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstUnaryOperator.cs @@ -58,6 +58,7 @@ internal enum AstUnaryOperator Round, SetIntersection, SetUnion, + Sigmoid, Sin, Sinh, Size, @@ -161,6 +162,7 @@ public static string Render(this AstUnaryOperator @operator) AstUnaryOperator.Round => "$round", AstUnaryOperator.SetIntersection => "$setIntersection", AstUnaryOperator.SetUnion => "$setUnion", + AstUnaryOperator.Sigmoid => "$sigmoid", AstUnaryOperator.Sin => "$sin", AstUnaryOperator.Sinh => "$sinh", AstUnaryOperator.Size => "$size", diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/MqlMethod.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/MqlMethod.cs index e04f8d50d13..be28eece135 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 __sigmoid; // 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)); + __sigmoid = ReflectionInfo.Method((double value) => Mql.Sigmoid(value)); } // public properties @@ -60,5 +62,6 @@ static MqlMethod() public static MethodInfo Field => __field; public static MethodInfo IsMissing => __isMissing; public static MethodInfo IsNullOrMissing => __isNullOrMissing; + public static MethodInfo Sigmoid => __sigmoid; } } diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodCallExpressionToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodCallExpressionToAggregationExpressionTranslator.cs index 5eb324df3fd..41786e4cb00 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodCallExpressionToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodCallExpressionToAggregationExpressionTranslator.cs @@ -77,6 +77,7 @@ public static TranslatedExpression Translate(TranslationContext context, MethodC case "SequenceEqual": return SequenceEqualMethodToAggregationExpressionTranslator.Translate(context, expression); case "SetEquals": return SetEqualsMethodToAggregationExpressionTranslator.Translate(context, expression); case "Shift": return ShiftMethodToAggregationExpressionTranslator.Translate(context, expression); + case "Sigmoid": return SigmoidMethodToAggregationExpressionTranslator.Translate(context, expression); case "Split": return SplitMethodToAggregationExpressionTranslator.Translate(context, expression); case "Sqrt": return SqrtMethodToAggregationExpressionTranslator.Translate(context, expression); case "StrLenBytes": return StrLenBytesMethodToAggregationExpressionTranslator.Translate(context, expression); diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/SigmoidMethodToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/SigmoidMethodToAggregationExpressionTranslator.cs new file mode 100644 index 00000000000..12f42cefb9e --- /dev/null +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/SigmoidMethodToAggregationExpressionTranslator.cs @@ -0,0 +1,47 @@ +/* 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; +using System.Linq.Expressions; +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 static class SigmoidMethodToAggregationExpressionTranslator + { + public static TranslatedExpression Translate(TranslationContext context, MethodCallExpression expression) + { + var method = expression.Method; + var arguments = expression.Arguments; + + if (method.Is(MqlMethod.Sigmoid)) + { + var valueExpression = arguments.Single(); + var valueTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, valueExpression); + SerializationHelper.EnsureRepresentationIsNumeric(expression, valueExpression, valueTranslation); + + return new TranslatedExpression( + expression, + AstExpression.Unary(AstUnaryOperator.Sigmoid, valueTranslation.Ast), + DoubleSerializer.Instance); + } + + throw new ExpressionNotSupportedException(expression); + } + } +} \ No newline at end of file diff --git a/src/MongoDB.Driver/Mql.cs b/src/MongoDB.Driver/Mql.cs index e1ccf7ef4ce..b3758ceebe6 100644 --- a/src/MongoDB.Driver/Mql.cs +++ b/src/MongoDB.Driver/Mql.cs @@ -152,5 +152,15 @@ public static bool IsNullOrMissing(TField field) { throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); } + + /// + /// Transforms a real-valued input into a value between 0 and 1 using the $sigmoid operator. + /// + /// The input value. + /// The transformed value. + public static double Sigmoid(double value) + { + throw CustomLinqExtensionMethodHelper.CreateNotSupportedException(); + } } } diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/SigmoidMethodToAggregationExpressionTranslatorTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/SigmoidMethodToAggregationExpressionTranslatorTests.cs new file mode 100644 index 00000000000..20a7f258cdf --- /dev/null +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/SigmoidMethodToAggregationExpressionTranslatorTests.cs @@ -0,0 +1,88 @@ +/* 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.Collections.Generic; +using System.Linq; +using FluentAssertions; +using MongoDB.Bson; +using MongoDB.Bson.Serialization.Attributes; +using MongoDB.Driver.Core.Misc; +using MongoDB.Driver.Linq; +using MongoDB.Driver.TestHelpers; +using Xunit; + +namespace MongoDB.Driver.Tests.Linq.Linq3Implementation.Translators.ExpressionToAggregationExpressionTranslators.MethodTranslators +{ + public class SigmoidMethodToAggregationExpressionTranslatorTests : LinqIntegrationTest + { + public SigmoidMethodToAggregationExpressionTranslatorTests(ClassFixture fixture) + : base(fixture, server => server.Supports(Feature.SigmoidOperator)) + { + } + + [Fact] + public void Sigmoid_should_work() + { + var collection = Fixture.Collection; + + var queryable = collection + .AsQueryable() + .Select(x => Mql.Sigmoid(x.X)); + + var stages = Translate(collection, queryable); + AssertStages(stages, "{ $project : { _v : { $sigmoid : '$X' }, _id : 0 } }"); + + var result = queryable.ToList(); + result.Should().BeEquivalentTo(new[] { 0.7310585786300049, 0.9933071490757153, 0.999997739675702, 0.9999999992417439}); + } + + [Fact] + public void Sigmoid_with_non_numeric_representation_should_throw() + { + var exception = Record.Exception(() => + { + var collection = Fixture.Collection; + + var queryable = collection + .AsQueryable() + .Select(x => Mql.Sigmoid(x.Y)); + + Translate(collection, queryable); + }); + + exception.Should().BeOfType(); + exception?.Message.Should().Contain("uses a non-numeric representation"); + } + + public class C + { + [BsonRepresentation(BsonType.String)] + public double Y { get; set; } + public double X { get; set; } + } + + public sealed class ClassFixture : MongoCollectionFixture + { + protected override IEnumerable InitialData => + [ + new() { X = 1.0 }, + new() { X = 5.0 }, + new() { X = 13.0 }, + new() { X = 21.0 }, + ]; + } + } +} \ No newline at end of file