Skip to content

Commit 081e408

Browse files
authored
CSHARP-5552: Add support for $convert in LINQ (mongodb#1659)
1 parent 167afae commit 081e408

File tree

18 files changed

+1087
-9
lines changed

18 files changed

+1087
-9
lines changed

src/MongoDB.Bson/Serialization/Serializers/ByteSerializer.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,16 @@ namespace MongoDB.Bson.Serialization.Serializers
2323
/// </summary>
2424
public sealed class ByteSerializer : StructSerializerBase<byte>, IRepresentationConfigurable<ByteSerializer>
2525
{
26+
#region static
27+
private static readonly ByteSerializer __instance = new();
28+
29+
// public static properties
30+
/// <summary>
31+
/// Gets a cached instance of a default ByteSerializer.
32+
/// </summary>
33+
public static ByteSerializer Instance => __instance;
34+
#endregion
35+
2636
// private fields
2737
private readonly BsonType _representation;
2838

src/MongoDB.Bson/Serialization/Serializers/Int16Serializer.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,15 @@ namespace MongoDB.Bson.Serialization.Serializers
2424
/// </summary>
2525
public sealed class Int16Serializer : StructSerializerBase<short>, IRepresentationConfigurable<Int16Serializer>, IRepresentationConverterConfigurable<Int16Serializer>
2626
{
27+
#region static
28+
private static readonly Int16Serializer __instance = new();
29+
30+
/// <summary>
31+
/// Gets a cached instance of an Int16Serializer;
32+
/// </summary>
33+
public static Int16Serializer Instance => __instance;
34+
#endregion
35+
2736
// private fields
2837
private readonly BsonType _representation;
2938
private readonly RepresentationConverter _converter;

src/MongoDB.Bson/Serialization/Serializers/SByteSerializer.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,15 @@ namespace MongoDB.Bson.Serialization.Serializers
2424
[CLSCompliant(false)]
2525
public sealed class SByteSerializer : StructSerializerBase<sbyte>, IRepresentationConfigurable<SByteSerializer>
2626
{
27+
#region static
28+
private static readonly SByteSerializer __instance = new();
29+
30+
/// <summary>
31+
/// Gets a cached instance of an SByteSerializer;
32+
/// </summary>
33+
public static SByteSerializer Instance => __instance;
34+
#endregion
35+
2736
// private fields
2837
private readonly BsonType _representation;
2938

src/MongoDB.Bson/Serialization/Serializers/SingleSerializer.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,15 @@ namespace MongoDB.Bson.Serialization.Serializers
2424
/// </summary>
2525
public sealed class SingleSerializer : StructSerializerBase<float>, IRepresentationConfigurable<SingleSerializer>, IRepresentationConverterConfigurable<SingleSerializer>
2626
{
27+
#region static
28+
private static readonly SingleSerializer __instance = new();
29+
30+
/// <summary>
31+
/// Gets a cached instance of an SingleSerializer;
32+
/// </summary>
33+
public static SingleSerializer Instance => __instance;
34+
#endregion
35+
2736
// private fields
2837
private readonly BsonType _representation;
2938
private readonly RepresentationConverter _converter;

src/MongoDB.Bson/Serialization/Serializers/UInt16Serializer.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,15 @@ namespace MongoDB.Bson.Serialization.Serializers
2525
[CLSCompliant(false)]
2626
public sealed class UInt16Serializer : StructSerializerBase<ushort>, IRepresentationConfigurable<UInt16Serializer>, IRepresentationConverterConfigurable<UInt16Serializer>
2727
{
28+
#region static
29+
private static readonly UInt16Serializer __instance = new();
30+
31+
/// <summary>
32+
/// Gets a cached instance of an UInt16Serializer;
33+
/// </summary>
34+
public static UInt16Serializer Instance => __instance;
35+
#endregion
36+
2837
// private fields
2938
private readonly BsonType _representation;
3039
private readonly RepresentationConverter _converter;

src/MongoDB.Bson/Serialization/Serializers/UInt64Serializer.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,15 @@ namespace MongoDB.Bson.Serialization.Serializers
2525
[CLSCompliant(false)]
2626
public sealed class UInt64Serializer : StructSerializerBase<ulong>, IRepresentationConfigurable<UInt64Serializer>, IRepresentationConverterConfigurable<UInt64Serializer>
2727
{
28+
#region static
29+
private static readonly UInt64Serializer __instance = new();
30+
31+
/// <summary>
32+
/// Gets a cached instance of an UInt64Serializer;
33+
/// </summary>
34+
public static UInt64Serializer Instance => __instance;
35+
#endregion
36+
2837
// private fields
2938
private readonly BsonType _representation;
3039
private readonly RepresentationConverter _converter;

src/MongoDB.Driver/ConvertOptions.cs

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
/* Copyright 2010-present MongoDB Inc.
2+
*
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS,
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*/
15+
16+
using MongoDB.Bson;
17+
18+
namespace MongoDB.Driver
19+
{
20+
/// <summary>
21+
/// Represents the options parameter for <see cref="Mql.Convert{TFrom, TTo}(TFrom, ConvertOptions{TTo})"/>.
22+
/// </summary>
23+
public abstract class ConvertOptions
24+
{
25+
private ByteOrder? _byteOrder;
26+
private string _format;
27+
private BsonBinarySubType? _subType;
28+
29+
/// <summary>
30+
/// The byteOrder parameter.
31+
/// </summary>
32+
public ByteOrder? ByteOrder
33+
{
34+
get => _byteOrder;
35+
set => _byteOrder = value;
36+
}
37+
38+
/// <summary>
39+
/// The format parameter.
40+
/// </summary>
41+
public string Format
42+
{
43+
get => _format;
44+
set => _format = value;
45+
}
46+
47+
/// <summary>
48+
/// The subType parameter.
49+
/// </summary>
50+
public BsonBinarySubType? SubType
51+
{
52+
get => _subType;
53+
set => _subType = value;
54+
}
55+
56+
internal abstract bool OnErrorWasSet(out object onError);
57+
58+
internal abstract bool OnNullWasSet(out object onNull);
59+
}
60+
61+
/// <summary>
62+
/// Represents the options parameter for <see cref="Mql.Convert{TFrom, TTo}(TFrom, ConvertOptions{TTo})"/>.
63+
/// This class allows to set 'onError' and 'onNull'.
64+
/// </summary>
65+
/// <typeparam name="TTo"> The type of 'onError' and 'onNull'.</typeparam>
66+
public class ConvertOptions<TTo> : ConvertOptions
67+
{
68+
private TTo _onError;
69+
private bool _onErrorWasSet;
70+
private TTo _onNull;
71+
private bool _onNullWasSet;
72+
73+
/// <summary>
74+
/// The onError parameter.
75+
/// </summary>
76+
public TTo OnError
77+
{
78+
get => _onError;
79+
set
80+
{
81+
_onError = value;
82+
_onErrorWasSet = true;
83+
}
84+
}
85+
86+
/// <summary>
87+
/// The onNull parameter.
88+
/// </summary>
89+
public TTo OnNull
90+
{
91+
get => _onNull;
92+
set
93+
{
94+
_onNull = value;
95+
_onNullWasSet = true;
96+
}
97+
}
98+
99+
internal override bool OnErrorWasSet(out object onError)
100+
{
101+
onError = _onError;
102+
return _onErrorWasSet;
103+
}
104+
105+
internal override bool OnNullWasSet(out object onNull)
106+
{
107+
onNull = _onNull;
108+
return _onNullWasSet;
109+
}
110+
}
111+
112+
/// <summary>
113+
/// Represents the byte order of binary data when converting to/from numerical types using <see cref="Mql.Convert{TFrom, TTo}(TFrom, ConvertOptions{TTo})"/>.
114+
/// </summary>
115+
public enum ByteOrder
116+
{
117+
/// <summary>
118+
/// Big endian order.
119+
/// </summary>
120+
BigEndian,
121+
/// <summary>
122+
/// Little endian order.
123+
/// </summary>
124+
LittleEndian,
125+
}
126+
}

src/MongoDB.Driver/Core/Misc/Feature.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ public class Feature
4444
private static readonly Feature __clientBulkWrite = new Feature("ClientBulkWrite", WireVersion.Server80);
4545
private static readonly Feature __clientSideEncryption = new Feature("ClientSideEncryption", WireVersion.Server42);
4646
private static readonly Feature __clusteredIndexes = new Feature("ClusteredIndexes", WireVersion.Server53);
47+
private static readonly Feature __convertOperatorBinDataToFromNumeric = new Feature("ConvertOperatorBinDataToFromNumeric", WireVersion.Server81);
48+
private static readonly Feature __convertOperatorBinDataToFromString= new Feature("ConvertOperatorBinDataToFromString", WireVersion.Server80);
4749
private static readonly Feature __createIndexCommitQuorum = new Feature("CreateIndexCommitQuorum", WireVersion.Server44);
4850
private static readonly Feature __createIndexesUsingInsertOperations = new Feature("CreateIndexesUsingInsertOperations", WireVersion.Zero, WireVersion.Server42);
4951
private static readonly Feature __csfleRangeAlgorithm = new Feature("CsfleRangeAlgorithm", WireVersion.Server62);
@@ -195,6 +197,16 @@ public class Feature
195197
/// </summary>
196198
public static Feature ClusteredIndexes => __clusteredIndexes;
197199

200+
/// <summary>
201+
/// Gets the conversion of binary data to/from numeric types feature.
202+
/// </summary>
203+
public static Feature ConvertOperatorBinDataToFromNumeric => __convertOperatorBinDataToFromNumeric;
204+
205+
/// <summary>
206+
/// Gets the conversion of binary data to/from string feature.
207+
/// </summary>
208+
public static Feature ConvertOperatorBinDataToFromString => __convertOperatorBinDataToFromString;
209+
198210
/// <summary>
199211
/// Gets the create index commit quorum feature.
200212
/// </summary>
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/* Copyright 2010-present MongoDB Inc.
2+
*
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS,
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*/
15+
16+
using System;
17+
using MongoDB.Bson;
18+
19+
namespace MongoDB.Driver.Linq.Linq3Implementation.Ast
20+
{
21+
internal static class AstEnumExtensions
22+
{
23+
public static string Render(this BsonType type)
24+
{
25+
return type switch
26+
{
27+
BsonType.Array => "array",
28+
BsonType.Binary => "binData",
29+
BsonType.Boolean => "bool",
30+
BsonType.DateTime => "date",
31+
BsonType.Decimal128 => "decimal",
32+
BsonType.Document => "object",
33+
BsonType.Double => "double",
34+
BsonType.Int32 => "int",
35+
BsonType.Int64 => "long",
36+
BsonType.JavaScript => "javascript",
37+
BsonType.JavaScriptWithScope => "javascriptWithScope",
38+
BsonType.MaxKey => "maxKey",
39+
BsonType.MinKey => "minKey",
40+
BsonType.Null => "null",
41+
BsonType.ObjectId => "objectId",
42+
BsonType.RegularExpression => "regex",
43+
BsonType.String => "string",
44+
BsonType.Symbol => "symbol",
45+
BsonType.Timestamp => "timestamp",
46+
BsonType.Undefined => "undefined",
47+
_ => throw new ArgumentException($"Unexpected BSON type: {type}.", nameof(type))
48+
};
49+
}
50+
51+
public static string Render(this ByteOrder byteOrder)
52+
{
53+
return byteOrder switch
54+
{
55+
ByteOrder.BigEndian => "big",
56+
ByteOrder.LittleEndian => "little",
57+
_ => throw new ArgumentException($"Unexpected {nameof(ByteOrder)}: {byteOrder}.", nameof(byteOrder))
58+
};
59+
}
60+
}
61+
}

src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstConvertExpression.cs

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,27 +21,39 @@ namespace MongoDB.Driver.Linq.Linq3Implementation.Ast.Expressions
2121
{
2222
internal sealed class AstConvertExpression : AstExpression
2323
{
24+
private readonly ByteOrder? _byteOrder;
25+
private readonly string _format;
2426
private readonly AstExpression _input;
2527
private readonly AstExpression _onError;
2628
private readonly AstExpression _onNull;
29+
private readonly BsonBinarySubType? _subType;
2730
private readonly AstExpression _to;
2831

2932
public AstConvertExpression(
3033
AstExpression input,
3134
AstExpression to,
35+
BsonBinarySubType? subType = null,
36+
ByteOrder? byteOrder = null,
37+
string format = null,
3238
AstExpression onError = null,
3339
AstExpression onNull = null)
3440
{
3541
_input = Ensure.IsNotNull(input, nameof(input));
3642
_to = Ensure.IsNotNull(to, nameof(to));
43+
_subType = subType;
44+
_byteOrder = byteOrder;
45+
_format = format;
3746
_onError = onError;
3847
_onNull = onNull;
3948
}
4049

50+
public ByteOrder? ByteOrder => _byteOrder;
51+
public string Format => _format;
4152
public AstExpression Input => _input;
4253
public override AstNodeType NodeType => AstNodeType.ConvertExpression;
4354
public AstExpression OnError => _onError;
4455
public AstExpression OnNull => _onNull;
56+
public BsonBinarySubType? SubType => _subType;
4557
public AstExpression To => _to;
4658

4759
public override AstNode Accept(AstNodeVisitor visitor)
@@ -56,9 +68,18 @@ public override BsonValue Render()
5668
{ "$convert", new BsonDocument
5769
{
5870
{ "input", _input.Render() },
59-
{ "to", _to.Render() },
71+
{ "to", _to.Render(), _subType == null },
72+
{ "to", () => new BsonDocument
73+
{
74+
{ "type", _to.Render() },
75+
{ "subtype", (int)_subType!.Value},
76+
},
77+
_subType != null
78+
},
6079
{ "onError", () => _onError.Render(), _onError != null },
61-
{ "onNull", () => _onNull.Render(), _onNull != null }
80+
{ "onNull", () => _onNull.Render(), _onNull != null },
81+
{ "format", () => _format, _format != null },
82+
{ "byteOrder", () => _byteOrder!.Value.Render(), _byteOrder != null }
6283
}
6384
}
6485
};
@@ -75,7 +96,7 @@ public AstConvertExpression Update(
7596
return this;
7697
}
7798

78-
return new AstConvertExpression(input, to, onError, onNull);
99+
return new AstConvertExpression(input, to, _subType, _byteOrder, _format, onError, onNull);
79100
}
80101
}
81102
}

0 commit comments

Comments
 (0)