Skip to content

Commit 36eb1fe

Browse files
committed
CSHARP-4220: Add support for $documents stage.
1 parent 0cf003d commit 36eb1fe

File tree

7 files changed

+373
-11
lines changed

7 files changed

+373
-11
lines changed

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ public class Feature
6161
private static readonly Feature __currentOpCommand = new Feature("CurrentOpCommand", WireVersion.Server32);
6262
private static readonly Feature __dateOperatorsNewIn50 = new Feature("DateOperatorsNewIn50", WireVersion.Server50);
6363
private static readonly Feature __densifyStage = new Feature("DensifyStage", WireVersion.Server51);
64+
private static readonly Feature __documentsStage = new Feature("DocumentsStage", WireVersion.Server51);
6465
private static readonly Feature __documentValidation = new Feature("DocumentValidation", WireVersion.Server32);
6566
private static readonly Feature __directConnectionSetting = new Feature("DirectConnectionSetting", WireVersion.Server44);
6667
private static readonly Feature __eval = new Feature("Eval", WireVersion.Zero, WireVersion.Server42);
@@ -334,6 +335,11 @@ public class Feature
334335
/// </summary>
335336
public static Feature DensifyStage => __densifyStage;
336337

338+
/// <summary>
339+
/// Gets the documents stage feature.
340+
/// </summary>
341+
public static Feature DocumentsStage => __documentsStage;
342+
337343
/// <summary>
338344
/// Gets the document validation feature.
339345
/// </summary>

src/MongoDB.Driver/AggregateExpressionDefinition.cs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,13 @@
1414
*/
1515

1616
using System;
17+
using System.Collections.Generic;
1718
using System.Linq.Expressions;
1819
using MongoDB.Bson;
1920
using MongoDB.Bson.Serialization;
2021
using MongoDB.Driver.Core.Misc;
2122
using MongoDB.Driver.Linq;
23+
using MongoDB.Driver.Linq.Linq3Implementation.Misc;
2224
using MongoDB.Driver.Linq.Linq3Implementation.Translators;
2325

2426
namespace MongoDB.Driver
@@ -147,4 +149,38 @@ public override BsonValue Render(IBsonSerializer<TSource> sourceSerializer, IBso
147149
return linqProvider.GetAdapter().TranslateExpressionToAggregateExpression(_expression, sourceSerializer, serializerRegistry, _translationOptions, contextData);
148150
}
149151
}
152+
153+
/// <summary>
154+
/// An aggregate expression for the $documents stage.
155+
/// </summary>
156+
/// <typeparam name="TDocument">The type of the documents.</typeparam>
157+
/// <seealso cref="MongoDB.Driver.AggregateExpressionDefinition{TSource, TResult}" />
158+
public sealed class DocumentsAggregateExpressionDefinition<TDocument> : AggregateExpressionDefinition<NoPipelineInput, IEnumerable<TDocument>>
159+
{
160+
// private fields
161+
private readonly IReadOnlyList<TDocument> _documents;
162+
private readonly IBsonSerializer<TDocument> _documentSerializer;
163+
164+
// constructors
165+
/// <summary>
166+
/// Initializes a new instance of the <see cref="ExpressionAggregateExpressionDefinition{TSource, TResult}" /> class.
167+
/// </summary>
168+
/// <param name="documents">The documents.</param>
169+
/// <param name="documentSerializer">The document serializer.</param>
170+
public DocumentsAggregateExpressionDefinition(
171+
IEnumerable<TDocument> documents,
172+
IBsonSerializer<TDocument> documentSerializer = null)
173+
{
174+
_documents = Ensure.IsNotNull(documents, nameof(documents)).AsReadOnlyList();
175+
_documentSerializer = documentSerializer; // can be null
176+
}
177+
178+
// public methods
179+
/// <inheritdoc/>
180+
public override BsonValue Render(IBsonSerializer<NoPipelineInput> sourceSerializer, IBsonSerializerRegistry serializerRegistry, LinqProvider linqProvider)
181+
{
182+
var documentSerializer = _documentSerializer ?? serializerRegistry.GetSerializer<TDocument>();
183+
return SerializationHelper.SerializeValues(documentSerializer, _documents);
184+
}
185+
}
150186
}

src/MongoDB.Driver/IAggregateFluentExtensions.cs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,38 @@ public static IAggregateFluent<TResult> Densify<TResult>(
159159
return aggregate.AppendStage(PipelineStageDefinitionBuilder.Densify(field, range, partitionByFields));
160160
}
161161

162+
/// <summary>
163+
/// Appends a $documents stage to the pipeline.
164+
/// </summary>
165+
/// <param name="aggregate">The aggregate.</param>
166+
/// <param name="documents">The documents.</param>
167+
/// <param name="documentSerializer">The document serializer.</param>
168+
/// <returns>The fluent aggregate interface.</returns>
169+
public static IAggregateFluent<TResult> Documents<TResult>(
170+
this IAggregateFluent<NoPipelineInput> aggregate,
171+
AggregateExpressionDefinition<NoPipelineInput, IEnumerable<TResult>> documents,
172+
IBsonSerializer<TResult> documentSerializer = null)
173+
{
174+
Ensure.IsNotNull(aggregate, nameof(aggregate));
175+
return aggregate.AppendStage(PipelineStageDefinitionBuilder.Documents(documents, documentSerializer));
176+
}
177+
178+
/// <summary>
179+
/// Appends a $documents stage to the pipeline.
180+
/// </summary>
181+
/// <param name="aggregate">The aggregate.</param>
182+
/// <param name="documents">The documents.</param>
183+
/// <param name="documentSerializer">The document serializer.</param>
184+
/// <returns>The fluent aggregate interface.</returns>
185+
public static IAggregateFluent<TResult> Documents<TResult>(
186+
this IAggregateFluent<NoPipelineInput> aggregate,
187+
IEnumerable<TResult> documents,
188+
IBsonSerializer<TResult> documentSerializer = null)
189+
{
190+
Ensure.IsNotNull(aggregate, nameof(aggregate));
191+
return aggregate.AppendStage(PipelineStageDefinitionBuilder.Documents(documents, documentSerializer));
192+
}
193+
162194
/// <summary>
163195
/// Appends a $facet stage to the pipeline.
164196
/// </summary>

src/MongoDB.Driver/PipelineDefinitionBuilder.cs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,44 @@ public static PipelineDefinition<TInput, TOutput> Densify<TInput, TOutput>(
397397
return pipeline.AppendStage(PipelineStageDefinitionBuilder.Densify(field, range, partitionByFields));
398398
}
399399

400+
/// <summary>
401+
/// Appends a $documents stage to the pipeline.
402+
/// </summary>
403+
/// <typeparam name="TDocument">The type of the documents.</typeparam>
404+
/// <param name="pipeline">The pipeline.</param>
405+
/// <param name="documents">The documents.</param>
406+
/// <param name="documentSerializer">The document serializer.</param>
407+
/// <returns>
408+
/// A new pipeline with an additional stage.
409+
/// </returns>
410+
public static PipelineDefinition<NoPipelineInput, TDocument> Documents<TDocument>(
411+
this PipelineDefinition<NoPipelineInput, NoPipelineInput> pipeline,
412+
AggregateExpressionDefinition<NoPipelineInput, IEnumerable<TDocument>> documents,
413+
IBsonSerializer<TDocument> documentSerializer = null)
414+
{
415+
Ensure.IsNotNull(pipeline, nameof(pipeline));
416+
return pipeline.AppendStage(PipelineStageDefinitionBuilder.Documents(documents, documentSerializer));
417+
}
418+
419+
/// <summary>
420+
/// Appends a $documents stage to the pipeline.
421+
/// </summary>
422+
/// <typeparam name="TDocument">The type of the documents.</typeparam>
423+
/// <param name="pipeline">The pipeline.</param>
424+
/// <param name="documents">The documents.</param>
425+
/// <param name="documentSerializer">The document serializer.</param>
426+
/// <returns>
427+
/// A new pipeline with an additional stage.
428+
/// </returns>
429+
public static PipelineDefinition<NoPipelineInput, TDocument> Documents<TDocument>(
430+
this PipelineDefinition<NoPipelineInput, NoPipelineInput> pipeline,
431+
IEnumerable<TDocument> documents,
432+
IBsonSerializer<TDocument> documentSerializer = null)
433+
{
434+
Ensure.IsNotNull(pipeline, nameof(pipeline));
435+
return pipeline.AppendStage(PipelineStageDefinitionBuilder.Documents(documents, documentSerializer));
436+
}
437+
400438
/// <summary>
401439
/// Appends a $facet stage to the pipeline.
402440
/// </summary>

src/MongoDB.Driver/PipelineStageDefinitionBuilder.cs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
using MongoDB.Bson.Serialization;
2323
using MongoDB.Driver.Core.Misc;
2424
using MongoDB.Driver.Linq;
25+
using MongoDB.Driver.Linq.Linq3Implementation.Misc;
2526
using MongoDB.Driver.Linq.Linq3Implementation.Serializers;
2627
using MongoDB.Driver.Linq.Linq3Implementation.Translators;
2728

@@ -477,6 +478,52 @@ public static PipelineStageDefinition<TInput, TInput> Densify<TInput>(
477478
return Densify(field, range, (IEnumerable<Expression<Func<TInput, object>>>)partitionByFields);
478479
}
479480

481+
/// <summary>
482+
/// Creates a $documents stage.
483+
/// </summary>
484+
/// <typeparam name="TDocument">The type of the documents.</typeparam>
485+
/// <param name="documents">The documents.</param>
486+
/// <param name="documentSerializer">The document serializer.</param>
487+
/// <returns>The stage.</returns>
488+
public static PipelineStageDefinition<NoPipelineInput, TDocument> Documents<TDocument>(
489+
AggregateExpressionDefinition<NoPipelineInput, IEnumerable<TDocument>> documents,
490+
IBsonSerializer<TDocument> documentSerializer = null)
491+
{
492+
if (typeof(TDocument) == typeof(NoPipelineInput))
493+
{
494+
throw new ArgumentException("Documents cannot be of type NoPipelineInput.", nameof(documents));
495+
}
496+
497+
const string operatorName = "$documents";
498+
var stage = new DelegatedPipelineStageDefinition<NoPipelineInput, TDocument>(
499+
operatorName,
500+
(s, sr, linqProvider) =>
501+
{
502+
var renderedDocuments = documents.Render(NoPipelineInputSerializer.Instance, sr, linqProvider);
503+
return new RenderedPipelineStageDefinition<TDocument>(
504+
operatorName,
505+
new BsonDocument(operatorName, renderedDocuments),
506+
documentSerializer ?? sr.GetSerializer<TDocument>());
507+
});
508+
509+
return stage;
510+
}
511+
512+
/// <summary>
513+
/// Creates a $documents stage.
514+
/// </summary>
515+
/// <typeparam name="TDocument">The type of the documents.</typeparam>
516+
/// <param name="documents">The documents.</param>
517+
/// <param name="documentSerializer">The document serializer.</param>
518+
/// <returns>The stage.</returns>
519+
public static PipelineStageDefinition<NoPipelineInput, TDocument> Documents<TDocument>(
520+
IEnumerable<TDocument> documents,
521+
IBsonSerializer<TDocument> documentSerializer = null)
522+
{
523+
var aggregateExpression = new DocumentsAggregateExpressionDefinition<TDocument>(documents, documentSerializer);
524+
return Documents(aggregateExpression, documentSerializer);
525+
}
526+
480527
/// <summary>
481528
/// Creates a $facet stage.
482529
/// </summary>

0 commit comments

Comments
 (0)