Skip to content

Commit 01b378f

Browse files
craiggwilsonrstam
authored andcommitted
CSHARP-1622: added support for $substrCP.
1 parent ee97df7 commit 01b378f

12 files changed

+149
-31
lines changed

src/MongoDB.Driver/AggregateOptions.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ public class AggregateOptions
3232
private int? _batchSize;
3333
private bool? _bypassDocumentValidation;
3434
private TimeSpan? _maxTime;
35+
private ExpressionTranslationOptions _translationOptions;
3536
private bool? _useCursor;
3637

3738
// properties
@@ -71,6 +72,15 @@ public TimeSpan? MaxTime
7172
set { _maxTime = value; }
7273
}
7374

75+
/// <summary>
76+
/// Gets or sets the translation options.
77+
/// </summary>
78+
public ExpressionTranslationOptions TranslationOptions
79+
{
80+
get { return _translationOptions; }
81+
set { _translationOptions = value; }
82+
}
83+
7484
/// <summary>
7585
/// Gets or sets a value indicating whether to use a cursor.
7686
/// </summary>
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/* Copyright 2016 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+
17+
namespace MongoDB.Driver
18+
{
19+
/// <summary>
20+
/// Option for which expression to generate for certain string operations.
21+
/// </summary>
22+
public enum AggregateStringTranslationMode
23+
{
24+
/// <summary>
25+
/// Translate to the byte variation.
26+
/// </summary>
27+
Bytes,
28+
/// <summary>
29+
/// Translate to the code points variation. This is only supported in >= MongoDB 3.4.
30+
/// </summary>
31+
CodePoints
32+
}
33+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/* Copyright 2016 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+
17+
namespace MongoDB.Driver
18+
{
19+
/// <summary>
20+
/// Options for controlling translation from .NET expression trees into MongoDB expressions.
21+
/// </summary>
22+
public class ExpressionTranslationOptions
23+
{
24+
internal static ExpressionTranslationOptions Default = new ExpressionTranslationOptions();
25+
26+
/// <summary>
27+
/// Gets or sets the string translation mode.
28+
/// </summary>
29+
public AggregateStringTranslationMode? StringTranslationMode { get; set; }
30+
}
31+
}

src/MongoDB.Driver/IAggregateFluentExtensions.cs

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ public static IAggregateFluent<TNewResult> Group<TResult, TKey, TNewResult>(this
6666
Ensure.IsNotNull(id, nameof(id));
6767
Ensure.IsNotNull(group, nameof(group));
6868

69-
return aggregate.Group<TNewResult>(new GroupExpressionProjection<TResult, TKey, TNewResult>(id, group));
69+
return aggregate.Group<TNewResult>(new GroupExpressionProjection<TResult, TKey, TNewResult>(id, group, aggregate.Options.TranslationOptions));
7070
}
7171

7272
/// <summary>
@@ -188,7 +188,7 @@ public static IAggregateFluent<TNewResult> Project<TResult, TNewResult>(this IAg
188188
Ensure.IsNotNull(aggregate, nameof(aggregate));
189189
Ensure.IsNotNull(projection, nameof(projection));
190190

191-
return aggregate.Project<TNewResult>(new ProjectExpressionProjection<TResult, TNewResult>(projection));
191+
return aggregate.Project<TNewResult>(new ProjectExpressionProjection<TResult, TNewResult>(projection, aggregate.Options.TranslationOptions));
192192
}
193193

194194
/// <summary>
@@ -501,10 +501,12 @@ public static IAggregateFluent<TNewResult> Unwind<TResult, TNewResult>(this IAgg
501501
private sealed class ProjectExpressionProjection<TResult, TNewResult> : ProjectionDefinition<TResult, TNewResult>
502502
{
503503
private readonly Expression<Func<TResult, TNewResult>> _expression;
504+
private readonly ExpressionTranslationOptions _translationOptions;
504505

505-
public ProjectExpressionProjection(Expression<Func<TResult, TNewResult>> expression)
506+
public ProjectExpressionProjection(Expression<Func<TResult, TNewResult>> expression, ExpressionTranslationOptions translationOptions)
506507
{
507508
_expression = Ensure.IsNotNull(expression, nameof(expression));
509+
_translationOptions = translationOptions;
508510
}
509511

510512
public Expression<Func<TResult, TNewResult>> Expression
@@ -514,19 +516,21 @@ public Expression<Func<TResult, TNewResult>> Expression
514516

515517
public override RenderedProjectionDefinition<TNewResult> Render(IBsonSerializer<TResult> documentSerializer, IBsonSerializerRegistry serializerRegistry)
516518
{
517-
return AggregateProjectTranslator.Translate<TResult, TNewResult>(_expression, documentSerializer, serializerRegistry);
519+
return AggregateProjectTranslator.Translate<TResult, TNewResult>(_expression, documentSerializer, serializerRegistry, _translationOptions);
518520
}
519521
}
520522

521523
private sealed class GroupExpressionProjection<TResult, TKey, TNewResult> : ProjectionDefinition<TResult, TNewResult>
522524
{
523525
private readonly Expression<Func<TResult, TKey>> _idExpression;
524526
private readonly Expression<Func<IGrouping<TKey, TResult>, TNewResult>> _groupExpression;
527+
private readonly ExpressionTranslationOptions _translationOptions;
525528

526-
public GroupExpressionProjection(Expression<Func<TResult, TKey>> idExpression, Expression<Func<IGrouping<TKey, TResult>, TNewResult>> groupExpression)
529+
public GroupExpressionProjection(Expression<Func<TResult, TKey>> idExpression, Expression<Func<IGrouping<TKey, TResult>, TNewResult>> groupExpression, ExpressionTranslationOptions translationOptions)
527530
{
528531
_idExpression = Ensure.IsNotNull(idExpression, nameof(idExpression));
529532
_groupExpression = Ensure.IsNotNull(groupExpression, nameof(groupExpression));
533+
_translationOptions = translationOptions;
530534
}
531535

532536
public Expression<Func<TResult, TKey>> IdExpression
@@ -541,7 +545,7 @@ public Expression<Func<IGrouping<TKey, TResult>, TNewResult>> GroupExpression
541545

542546
public override RenderedProjectionDefinition<TNewResult> Render(IBsonSerializer<TResult> documentSerializer, IBsonSerializerRegistry serializerRegistry)
543547
{
544-
return AggregateGroupTranslator.Translate<TKey, TResult, TNewResult>(_idExpression, _groupExpression, documentSerializer, serializerRegistry);
548+
return AggregateGroupTranslator.Translate<TKey, TResult, TNewResult>(_idExpression, _groupExpression, documentSerializer, serializerRegistry, _translationOptions);
545549
}
546550
}
547551
}

src/MongoDB.Driver/Linq/MongoQueryProviderImpl.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ private Expression Prepare(Expression expression)
127127
private QueryableTranslation Translate(Expression expression)
128128
{
129129
var pipelineExpression = Prepare(expression);
130-
return QueryableTranslator.Translate(pipelineExpression, _collection.Settings.SerializerRegistry);
130+
return QueryableTranslator.Translate(pipelineExpression, _collection.Settings.SerializerRegistry, _options.TranslationOptions);
131131
}
132132
}
133133
}

src/MongoDB.Driver/Linq/Translators/AggregateGroupTranslator.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,20 +28,20 @@ namespace MongoDB.Driver.Linq.Translators
2828
{
2929
internal static class AggregateGroupTranslator
3030
{
31-
public static RenderedProjectionDefinition<TResult> Translate<TKey, TDocument, TResult>(Expression<Func<TDocument, TKey>> idProjector, Expression<Func<IGrouping<TKey, TDocument>, TResult>> groupProjector, IBsonSerializer<TDocument> parameterSerializer, IBsonSerializerRegistry serializerRegistry)
31+
public static RenderedProjectionDefinition<TResult> Translate<TKey, TDocument, TResult>(Expression<Func<TDocument, TKey>> idProjector, Expression<Func<IGrouping<TKey, TDocument>, TResult>> groupProjector, IBsonSerializer<TDocument> parameterSerializer, IBsonSerializerRegistry serializerRegistry, ExpressionTranslationOptions translationOptions)
3232
{
3333
var bindingContext = new PipelineBindingContext(serializerRegistry);
3434

3535
var keySelector = BindKeySelector(bindingContext, idProjector, parameterSerializer);
3636
var boundGroupExpression = BindGroup(bindingContext, groupProjector, parameterSerializer, keySelector);
3737

3838
var projectionSerializer = bindingContext.GetSerializer(boundGroupExpression.Type, boundGroupExpression);
39-
var projection = AggregateLanguageTranslator.Translate(boundGroupExpression).AsBsonDocument;
39+
var projection = AggregateLanguageTranslator.Translate(boundGroupExpression, translationOptions).AsBsonDocument;
4040

4141
// must have an "_id" in a group document
4242
if (!projection.Contains("_id"))
4343
{
44-
var idProjection = AggregateLanguageTranslator.Translate(keySelector);
44+
var idProjection = AggregateLanguageTranslator.Translate(keySelector, translationOptions);
4545
projection.InsertAt(0, new BsonElement("_id", idProjection));
4646
}
4747

src/MongoDB.Driver/Linq/Translators/AggregateLanguageTranslator.cs

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,18 @@ namespace MongoDB.Driver.Linq.Translators
3131
{
3232
internal sealed class AggregateLanguageTranslator
3333
{
34-
public static BsonValue Translate(Expression node)
34+
public static BsonValue Translate(Expression node, ExpressionTranslationOptions translationOptions)
3535
{
36-
var builder = new AggregateLanguageTranslator();
36+
var builder = new AggregateLanguageTranslator(translationOptions);
3737
return builder.TranslateValue(node);
3838
}
3939

40-
private AggregateLanguageTranslator()
40+
private readonly AggregateStringTranslationMode _stringTranslationMode;
41+
42+
private AggregateLanguageTranslator(ExpressionTranslationOptions translationOptions)
4143
{
44+
translationOptions = translationOptions ?? ExpressionTranslationOptions.Default;
45+
_stringTranslationMode = translationOptions.StringTranslationMode.GetValueOrDefault(AggregateStringTranslationMode.Bytes);
4246
}
4347

4448
private BsonValue TranslateValue(Expression node)
@@ -1119,7 +1123,12 @@ private bool TryTranslateStringMethodCall(MethodCallExpression node, out BsonVal
11191123
case "Substring":
11201124
if (node.Arguments.Count == 2)
11211125
{
1122-
result = new BsonDocument("$substr", new BsonArray(new[]
1126+
var name = "$substr";
1127+
if (_stringTranslationMode == AggregateStringTranslationMode.CodePoints)
1128+
{
1129+
name = "$substrCP";
1130+
}
1131+
result = new BsonDocument(name, new BsonArray(new[]
11231132
{
11241133
field,
11251134
TranslateValue(node.Arguments[0]),

src/MongoDB.Driver/Linq/Translators/AggregateProjectTranslator.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ namespace MongoDB.Driver.Linq.Translators
2525
{
2626
internal static class AggregateProjectTranslator
2727
{
28-
public static RenderedProjectionDefinition<TResult> Translate<TDocument, TResult>(Expression<Func<TDocument, TResult>> projector, IBsonSerializer<TDocument> parameterSerializer, IBsonSerializerRegistry serializerRegistry)
28+
public static RenderedProjectionDefinition<TResult> Translate<TDocument, TResult>(Expression<Func<TDocument, TResult>> projector, IBsonSerializer<TDocument> parameterSerializer, IBsonSerializerRegistry serializerRegistry, ExpressionTranslationOptions translationOptions)
2929
{
3030
var bindingContext = new PipelineBindingContext(serializerRegistry);
3131
var parameterExpression = new DocumentExpression(parameterSerializer);
@@ -36,14 +36,14 @@ public static RenderedProjectionDefinition<TResult> Translate<TDocument, TResult
3636
node = bindingContext.Bind(node);
3737

3838
var projectionSerializer = bindingContext.GetSerializer(node.Type, node);
39-
var projection = TranslateProject(node);
39+
var projection = TranslateProject(node, translationOptions);
4040

4141
return new RenderedProjectionDefinition<TResult>(projection, (IBsonSerializer<TResult>)projectionSerializer);
4242
}
4343

44-
public static BsonDocument TranslateProject(Expression expression)
44+
public static BsonDocument TranslateProject(Expression expression, ExpressionTranslationOptions translationOptions)
4545
{
46-
var projection = (BsonDocument)AggregateLanguageTranslator.Translate(expression);
46+
var projection = (BsonDocument)AggregateLanguageTranslator.Translate(expression, translationOptions);
4747
if (!projection.Contains("_id"))
4848
{
4949
projection.Add("_id", 0);

src/MongoDB.Driver/Linq/Translators/QueryableTranslator.cs

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,9 @@ namespace MongoDB.Driver.Linq.Translators
3030
{
3131
internal sealed class QueryableTranslator
3232
{
33-
public static QueryableTranslation Translate(Expression node, IBsonSerializerRegistry serializerRegistry)
33+
public static QueryableTranslation Translate(Expression node, IBsonSerializerRegistry serializerRegistry, ExpressionTranslationOptions translationOptions)
3434
{
35-
var translator = new QueryableTranslator(serializerRegistry);
35+
var translator = new QueryableTranslator(serializerRegistry, translationOptions);
3636
translator.Translate(node);
3737

3838
var outputType = translator._outputSerializer.ValueType;
@@ -53,10 +53,12 @@ public static QueryableTranslation Translate(Expression node, IBsonSerializerReg
5353
private readonly IBsonSerializerRegistry _serializerRegistry;
5454
private readonly List<BsonDocument> _stages;
5555
private IResultTransformer _resultTransformer;
56+
private ExpressionTranslationOptions _translationOptions;
5657

57-
public QueryableTranslator(IBsonSerializerRegistry serializerRegistry)
58+
public QueryableTranslator(IBsonSerializerRegistry serializerRegistry, ExpressionTranslationOptions translationOptions)
5859
{
5960
_serializerRegistry = Ensure.IsNotNull(serializerRegistry, nameof(serializerRegistry));
61+
_translationOptions = Ensure.IsNotNull(translationOptions, nameof(translationOptions));
6062
_stages = new List<BsonDocument>();
6163
}
6264

@@ -119,11 +121,11 @@ private void TranslateGroupBy(GroupByExpression node)
119121
Translate(node.Source);
120122

121123
var groupValue = new BsonDocument();
122-
var idValue = AggregateLanguageTranslator.Translate(node.KeySelector);
124+
var idValue = AggregateLanguageTranslator.Translate(node.KeySelector, _translationOptions);
123125
groupValue.Add("_id", idValue);
124126
foreach (var accumulator in node.Accumulators)
125127
{
126-
var accumulatorValue = AggregateLanguageTranslator.Translate(accumulator);
128+
var accumulatorValue = AggregateLanguageTranslator.Translate(accumulator, _translationOptions);
127129
groupValue.Add(accumulator.FieldName, accumulatorValue);
128130
}
129131

@@ -148,15 +150,15 @@ private void TranslateGroupJoin(GroupJoinExpression node)
148150
throw new NotSupportedException("Only a collection is allowed to be joined.");
149151
}
150152

151-
var localFieldValue = AggregateLanguageTranslator.Translate(node.SourceKeySelector);
153+
var localFieldValue = AggregateLanguageTranslator.Translate(node.SourceKeySelector, _translationOptions);
152154
if (localFieldValue.BsonType != BsonType.String)
153155
{
154156
throw new NotSupportedException("Could not translate the local field.");
155157
}
156158

157159
var localField = localFieldValue.ToString().Substring(1); // remove '$'
158160

159-
var foreignFieldValue = AggregateLanguageTranslator.Translate(node.JoinedKeySelector);
161+
var foreignFieldValue = AggregateLanguageTranslator.Translate(node.JoinedKeySelector, _translationOptions);
160162
if (foreignFieldValue.BsonType != BsonType.String)
161163
{
162164
throw new NotSupportedException("Could not translate the foreign field.");
@@ -183,15 +185,15 @@ private void TranslateJoin(JoinExpression node)
183185
throw new NotSupportedException("Only a collection is allowed to be joined.");
184186
}
185187

186-
var localFieldValue = AggregateLanguageTranslator.Translate(node.SourceKeySelector);
188+
var localFieldValue = AggregateLanguageTranslator.Translate(node.SourceKeySelector, _translationOptions);
187189
if (localFieldValue.BsonType != BsonType.String)
188190
{
189191
throw new NotSupportedException("Could not translate the local field.");
190192
}
191193

192194
var localField = localFieldValue.ToString().Substring(1); // remove '$'
193195

194-
var foreignFieldValue = AggregateLanguageTranslator.Translate(node.JoinedKeySelector);
196+
var foreignFieldValue = AggregateLanguageTranslator.Translate(node.JoinedKeySelector, _translationOptions);
195197
if (foreignFieldValue.BsonType != BsonType.String)
196198
{
197199
throw new NotSupportedException("Could not translate the foreign field.");
@@ -367,7 +369,7 @@ private void TranslateWhere(WhereExpression node)
367369
private BsonDocument TranslateProjectValue(Expression selector)
368370
{
369371
BsonDocument projectValue;
370-
var result = AggregateLanguageTranslator.Translate(selector);
372+
var result = AggregateLanguageTranslator.Translate(selector, _translationOptions);
371373
if (result.BsonType == BsonType.String)
372374
{
373375
// this means we got back a field expression prefixed with a $ sign.

src/MongoDB.Driver/MongoDB.Driver.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@
7676
</Compile>
7777
<Compile Include="AggregateFluent.cs" />
7878
<Compile Include="AggregateLookupOptions.cs" />
79+
<Compile Include="AggregateStringTranslationMode.cs" />
7980
<Compile Include="AsyncCursorHelper.cs" />
8081
<Compile Include="Builders.cs" />
8182
<Compile Include="CreateIndexModel.cs" />
@@ -255,6 +256,7 @@
255256
<Compile Include="Linq\Translators\ProjectedObjectDeserializer.cs" />
256257
<Compile Include="Linq\Translators\QueryableTranslator.cs" />
257258
<Compile Include="Linq\Translators\PredicateTranslator.cs" />
259+
<Compile Include="ExpressionTranslationOptions.cs" />
258260
<Compile Include="OfTypeSerializer.cs" />
259261
<Compile Include="PipelineStageDefinition.cs" />
260262
<Compile Include="PipelineDefinition.cs" />

0 commit comments

Comments
 (0)