Skip to content

Commit dd47c25

Browse files
JamesKovacskevbite
andauthored
CSHARP-2659: Adding support for $sample aggregation pipeline stage. (#1348)
Co-authored-by: Kevin Smith <[email protected]>
1 parent 08688b8 commit dd47c25

File tree

6 files changed

+82
-0
lines changed

6 files changed

+82
-0
lines changed

src/MongoDB.Driver/AggregateFluent.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,11 @@ public override IAggregateFluent<TNewResult> ReplaceWith<TNewResult>(AggregateEx
267267
return WithPipeline(_pipeline.ReplaceWith(newRoot));
268268
}
269269

270+
public override IAggregateFluent<TResult> Sample(long size)
271+
{
272+
return WithPipeline(_pipeline.Sample(size));
273+
}
274+
270275
public override IAggregateFluent<TResult> Search(
271276
SearchDefinition<TResult> searchDefinition,
272277
SearchHighlightOptions<TResult> highlight = null,

src/MongoDB.Driver/AggregateFluentBase.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,9 @@ public virtual IAggregateFluent<TNewResult> ReplaceWith<TNewResult>(AggregateExp
237237
throw new NotImplementedException();
238238
}
239239

240+
/// <inheritdoc />
241+
public virtual IAggregateFluent<TResult> Sample(long size) => throw new NotImplementedException();
242+
240243
/// <inheritdoc />
241244
public virtual IAggregateFluent<TResult> Search(
242245
SearchDefinition<TResult> searchDefinition,

src/MongoDB.Driver/IAggregateFluent.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,13 @@ IAggregateFluent<TNewResult> Lookup<TForeignDocument, TAsElement, TAs, TNewResul
381381
/// <returns>The fluent aggregate interface.</returns>
382382
IAggregateFluent<TNewResult> ReplaceWith<TNewResult>(AggregateExpressionDefinition<TResult, TNewResult> newRoot);
383383

384+
/// <summary>
385+
/// Appends a sample stage to the pipeline.
386+
/// </summary>
387+
/// <param name="size">The sample size.</param>
388+
/// <returns>The fluent aggregate interface.</returns>
389+
IAggregateFluent<TResult> Sample(long size);
390+
384391
/// <summary>
385392
/// Appends a $set stage to the pipeline.
386393
/// </summary>

src/MongoDB.Driver/PipelineDefinitionBuilder.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1097,6 +1097,24 @@ public static PipelineDefinition<TInput, TOutput> ReplaceWith<TInput, TIntermedi
10971097
return pipeline.AppendStage(PipelineStageDefinitionBuilder.ReplaceWith(newRoot, translationOptions));
10981098
}
10991099

1100+
/// <summary>
1101+
/// Appends a $sample stage to the pipeline.
1102+
/// </summary>
1103+
/// <typeparam name="TInput">The type of the input documents.</typeparam>
1104+
/// <typeparam name="TOutput">The type of the output documents.</typeparam>
1105+
/// <param name="pipeline">The pipeline.</param>
1106+
/// <param name="size">The sample size.</param>
1107+
/// <returns>
1108+
/// A new pipeline with an additional stage.
1109+
/// </returns>
1110+
public static PipelineDefinition<TInput, TOutput> Sample<TInput, TOutput>(
1111+
this PipelineDefinition<TInput, TOutput> pipeline,
1112+
long size)
1113+
{
1114+
Ensure.IsNotNull(pipeline, nameof(pipeline));
1115+
return pipeline.AppendStage(PipelineStageDefinitionBuilder.Sample<TOutput>(size));
1116+
}
1117+
11001118
/// <summary>
11011119
/// Appends a $search stage to the pipeline.
11021120
/// </summary>

src/MongoDB.Driver/PipelineStageDefinitionBuilder.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1757,6 +1757,19 @@ public static PipelineStageDefinition<TInput, TInput> Sort<TInput>(
17571757
return new SortPipelineStageDefinition<TInput>(sort);
17581758
}
17591759

1760+
/// <summary>
1761+
/// Creates a $sample stage.
1762+
/// </summary>
1763+
/// <typeparam name="TInput">The type of the input documents.</typeparam>
1764+
/// <param name="size">The size.</param>
1765+
/// <returns>The stage.</returns>
1766+
public static PipelineStageDefinition<TInput, TInput> Sample<TInput>(
1767+
long size)
1768+
{
1769+
Ensure.IsGreaterThanOrEqualToZero(size, nameof(size));
1770+
return new BsonDocumentPipelineStageDefinition<TInput, TInput>(new BsonDocument("$sample", new BsonDocument("size", size)));
1771+
}
1772+
17601773
/// <summary>
17611774
/// Creates a $sortByCount stage.
17621775
/// </summary>

tests/MongoDB.Driver.Tests/PipelineDefinitionBuilderTests.cs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,42 @@ public void Out_with_time_series_options_should_add_expected_stage()
158158
stages[0].Should().Be("{ $out: { db: 'database', coll: 'collection', timeseries: { timeField: 'time', metaField: 'symbol' } } }");
159159
}
160160

161+
[Theory]
162+
[InlineData(0)]
163+
[InlineData(15)]
164+
public void Sample_should_add_expected_stage(long size)
165+
{
166+
var pipeline = new EmptyPipelineDefinition<BsonDocument>();
167+
168+
var result = pipeline.Sample(size);
169+
170+
var stages = RenderStages(result, BsonDocumentSerializer.Instance);
171+
stages.Count.Should().Be(1);
172+
stages[0].Should().Be("{ $sample: { size: " + size + " } }");
173+
}
174+
175+
[Fact]
176+
public void Sample_should_throw_when_pipeline_is_null()
177+
{
178+
PipelineDefinition<BsonDocument, BsonDocument> pipeline = null;
179+
180+
var exception = Record.Exception(() => pipeline.Sample(15));
181+
182+
exception.Should().BeOfType<ArgumentNullException>()
183+
.Which.ParamName.Should().Be("pipeline");
184+
}
185+
186+
[Fact]
187+
public void Sample_should_throw_when_size_is_negative()
188+
{
189+
var pipeline = new EmptyPipelineDefinition<BsonDocument>();
190+
191+
var exception = Record.Exception(() => pipeline.Sample(-1));
192+
193+
exception.Should().BeOfType<ArgumentOutOfRangeException>()
194+
.Which.ParamName.Should().Be("size");
195+
}
196+
161197
[Fact]
162198
public void Search_should_add_expected_stage()
163199
{

0 commit comments

Comments
 (0)