Skip to content

CSHARP-3222: Add LINQ support for median and percentile accumulators/window functions #1743

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 14 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions src/MongoDB.Driver/Core/Misc/Feature.cs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ public class Feature
private static readonly Feature __loookupConciseSyntax = new Feature("LoookupConciseSyntax", WireVersion.Server50);
private static readonly Feature __loookupDocuments= new Feature("LoookupDocuments", WireVersion.Server60);
private static readonly Feature __mmapV1StorageEngine = new Feature("MmapV1StorageEngine", WireVersion.Zero, WireVersion.Server42);
private static readonly Feature __medianOperator = new Feature("MedianOperator", WireVersion.Server70);
private static readonly Feature __percentileOperator = new Feature("PercentileOperator", WireVersion.Server70);
private static readonly Feature __pickAccumulatorsNewIn52 = new Feature("PickAccumulatorsNewIn52", WireVersion.Server52);
private static readonly Feature __rankFusionStage = new Feature("RankFusionStage", WireVersion.Server81);
private static readonly Feature __regexMatch = new Feature("RegexMatch", WireVersion.Server42);
Expand Down Expand Up @@ -401,6 +403,16 @@ public class Feature
[Obsolete("This feature was removed in server version 4.2. As such, this property will be removed in a later release.")]
public static Feature MmapV1StorageEngine => __mmapV1StorageEngine;

/// <summary>
/// Gets the $median operator added in 7.0
/// </summary>
public static Feature MedianOperator => __medianOperator;

/// <summary>
/// Gets the $percentile operator added in 7.0
/// </summary>
public static Feature PercentileOperator => __percentileOperator;

/// <summary>
/// Gets the pick accumulators new in 5.2 feature.
/// </summary>
Expand Down
271 changes: 270 additions & 1 deletion src/MongoDB.Driver/Linq/ISetWindowFieldsPartitionExtensions.cs

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,9 @@ internal enum AstNodeType
MatchesEverythingFilter,
MatchesNothingFilter,
MatchStage,
MedianExpression,
MedianAccumulatorExpression,
MedianWindowExpression,
MergeStage,
ModFilterOperation,
NaryExpression,
Expand All @@ -104,6 +107,9 @@ internal enum AstNodeType
NullaryWindowExpression,
OrFilter,
OutStage,
PercentileExpression,
PercentileAccumulatorExpression,
PercentileWindowExpression,
PickAccumulatorExpression,
PickExpression,
Pipeline,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -597,6 +597,21 @@ public static AstExpression Max(AstExpression arg1, AstExpression arg2)
return new AstNaryExpression(AstNaryOperator.Max, [arg1, arg2]);
}

public static AstExpression Median(AstExpression input)
{
return new AstMedianExpression(input);
}

public static AstMedianAccumulatorExpression MedianAccumulator(AstExpression input)
{
return new AstMedianAccumulatorExpression(input);
}

public static AstMedianWindowExpression MedianWindowExpression(AstExpression input, AstWindow window)
{
return new AstMedianWindowExpression(input, window);
}

public static AstExpression Min(AstExpression array)
{
return new AstUnaryExpression(AstUnaryOperator.Min, array);
Expand Down Expand Up @@ -653,6 +668,21 @@ public static AstExpression Or(params AstExpression[] args)
return new AstNaryExpression(AstNaryOperator.Or, flattenedArgs);
}

public static AstPercentileExpression Percentile(AstExpression input, AstExpression percentiles)
{
return new AstPercentileExpression(input, percentiles);
}

public static AstPercentileAccumulatorExpression PercentileAccumulator(AstExpression input, AstExpression percentiles)
{
return new AstPercentileAccumulatorExpression(input, percentiles);
}

public static AstPercentileWindowExpression PercentileWindowExpression(AstExpression input, AstExpression percentiles, AstWindow window)
{
return new AstPercentileWindowExpression(input, percentiles, window);
}

public static AstExpression PickExpression(AstPickOperator @operator, AstExpression source, AstSortFields sortBy, AstVarExpression @as, AstExpression selector, AstExpression n)
{
return new AstPickExpression(@operator, source, sortBy, @as, selector, n);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/* 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 MongoDB.Bson;
using MongoDB.Driver.Core.Misc;
using MongoDB.Driver.Linq.Linq3Implementation.Ast.Visitors;

namespace MongoDB.Driver.Linq.Linq3Implementation.Ast.Expressions
{
internal sealed class AstMedianAccumulatorExpression : AstAccumulatorExpression
{
private readonly AstExpression _input;

public AstMedianAccumulatorExpression(AstExpression input)
{
_input = Ensure.IsNotNull(input, nameof(input));
}

public AstExpression Input => _input;

public override AstNodeType NodeType => AstNodeType.MedianAccumulatorExpression;

public override AstNode Accept(AstNodeVisitor visitor)
{
return visitor.VisitMedianAccumulatorExpression(this);
}

public override BsonValue Render()
{
return new BsonDocument
{
{
"$median", new BsonDocument
{
{ "input", _input.Render() },
{ "method", "approximate" } // server requires this parameter but currently only allows this value
}
}
};
}

public AstMedianAccumulatorExpression Update(AstExpression input)
{
if (input == _input)
{
return this;
}
return new AstMedianAccumulatorExpression(input);
}

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/* 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 MongoDB.Bson;
using MongoDB.Driver.Core.Misc;
using MongoDB.Driver.Linq.Linq3Implementation.Ast.Visitors;

namespace MongoDB.Driver.Linq.Linq3Implementation.Ast.Expressions
{
internal sealed class AstMedianExpression : AstExpression
{
private readonly AstExpression _input;

public AstMedianExpression(AstExpression input)
{
_input = Ensure.IsNotNull(input, nameof(input));
}

public AstExpression Input => _input;

public override AstNodeType NodeType => AstNodeType.MedianExpression;

public override AstNode Accept(AstNodeVisitor visitor)
{
return visitor.VisitMedianExpression(this);
}

public override BsonValue Render()
{
return new BsonDocument
{
{
"$median", new BsonDocument
{
{ "input", _input.Render() },
{ "method", "approximate" } // server requires this parameter but currently only allows this value
}
}
};
}

public AstMedianExpression Update(AstExpression input)
{
if (input == _input)
{
return this;
}
return new AstMedianExpression(input);
}

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/* 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 MongoDB.Bson;
using MongoDB.Driver.Core.Misc;
using MongoDB.Driver.Linq.Linq3Implementation.Ast.Visitors;

namespace MongoDB.Driver.Linq.Linq3Implementation.Ast.Expressions
{
internal sealed class AstMedianWindowExpression : AstWindowExpression
{
private readonly AstExpression _input;
private readonly AstWindow _window;

public AstMedianWindowExpression(AstExpression input, AstWindow window)
{
_input = Ensure.IsNotNull(input, nameof(input));
_window = window;
}

public AstExpression Input => _input;

public AstWindow Window => _window;

public override AstNodeType NodeType => AstNodeType.MedianWindowExpression;

public override AstNode Accept(AstNodeVisitor visitor)
{
return visitor.VisitMedianWindowExpression(this);
}

public override BsonValue Render()
{
return new BsonDocument
{
{
"$median", new BsonDocument
{
{ "input", _input.Render() },
{ "method", "approximate" } // server requires this parameter but currently only allows this value
}
},
{ "window", _window?.Render(), _window != null }
};
}

public AstMedianWindowExpression Update(AstExpression input, AstWindow window)
{
if (input == _input && window == _window)
{
return this;
}

return new AstMedianWindowExpression(input, window);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/* 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 MongoDB.Bson;
using MongoDB.Driver.Core.Misc;
using MongoDB.Driver.Linq.Linq3Implementation.Ast.Visitors;

namespace MongoDB.Driver.Linq.Linq3Implementation.Ast.Expressions
{
internal sealed class AstPercentileAccumulatorExpression : AstAccumulatorExpression
{
private readonly AstExpression _input;
private readonly AstExpression _percentiles;

public AstPercentileAccumulatorExpression(AstExpression input, AstExpression percentiles)
{
_input = Ensure.IsNotNull(input, nameof(input));
_percentiles = Ensure.IsNotNull(percentiles, nameof(percentiles));
}

public AstExpression Input => _input;

public AstExpression Percentiles => _percentiles;

public override AstNodeType NodeType => AstNodeType.PercentileAccumulatorExpression;

public override AstNode Accept(AstNodeVisitor visitor)
{
return visitor.VisitPercentileAccumulatorExpression(this);
}

public override BsonValue Render()
{
return new BsonDocument
{
{
"$percentile", new BsonDocument
{
{ "input", _input.Render() },
{ "p", _percentiles.Render() },
{ "method", "approximate" } // server requires this parameter but currently only allows this value
}
}
};
}

public AstPercentileAccumulatorExpression Update(AstExpression input, AstExpression percentiles)
{
if (input == _input && percentiles == _percentiles)
{
return this;
}
return new AstPercentileAccumulatorExpression(input, percentiles);
}
}
}
Loading