diff --git a/src/MongoDB.Driver/Core/Misc/Feature.cs b/src/MongoDB.Driver/Core/Misc/Feature.cs
index 47bf7bb65f9..d2a3d86e88a 100644
--- a/src/MongoDB.Driver/Core/Misc/Feature.cs
+++ b/src/MongoDB.Driver/Core/Misc/Feature.cs
@@ -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);
@@ -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;
+ ///
+ /// Gets the $median operator added in 7.0
+ ///
+ public static Feature MedianOperator => __medianOperator;
+
+ ///
+ /// Gets the $percentile operator added in 7.0
+ ///
+ public static Feature PercentileOperator => __percentileOperator;
+
///
/// Gets the pick accumulators new in 5.2 feature.
///
diff --git a/src/MongoDB.Driver/Linq/ISetWindowFieldsPartitionExtensions.cs b/src/MongoDB.Driver/Linq/ISetWindowFieldsPartitionExtensions.cs
index ca8aade374b..ecba3d5e81e 100644
--- a/src/MongoDB.Driver/Linq/ISetWindowFieldsPartitionExtensions.cs
+++ b/src/MongoDB.Driver/Linq/ISetWindowFieldsPartitionExtensions.cs
@@ -16,7 +16,6 @@
using System;
using System.Collections.Generic;
-using MongoDB.Bson;
namespace MongoDB.Driver.Linq
{
@@ -879,6 +878,136 @@ public static TValue Max(this ISetWindowFieldsPartition
throw new InvalidOperationException("This method is only intended to be used with SetWindowFields.");
}
+ ///
+ /// Returns the median of the numeric values. Median ignores non-numeric values.
+ ///
+ /// The type of the input documents in the partition.
+ /// The partition.
+ /// The selector that selects a value from the input document.
+ /// The window boundaries.
+ /// The median of the selected values.
+ public static decimal Median(this ISetWindowFieldsPartition partition, Func selector, SetWindowFieldsWindow window = null)
+ {
+ throw new InvalidOperationException("This method is only intended to be used with SetWindowFields.");
+ }
+
+ ///
+ /// Returns the median of the numeric values. Median ignores non-numeric values.
+ ///
+ /// The type of the input documents in the partition.
+ /// The partition.
+ /// The selector that selects a value from the input document.
+ /// The window boundaries.
+ /// The median of the selected values.
+ public static decimal? Median(this ISetWindowFieldsPartition partition, Func selector, SetWindowFieldsWindow window = null)
+ {
+ throw new InvalidOperationException("This method is only intended to be used with SetWindowFields.");
+ }
+
+ ///
+ /// Returns the median of the numeric values. Median ignores non-numeric values.
+ ///
+ /// The type of the input documents in the partition.
+ /// The partition.
+ /// The selector that selects a value from the input document.
+ /// The window boundaries.
+ /// The median of the selected values.
+ public static double Median(this ISetWindowFieldsPartition partition, Func selector, SetWindowFieldsWindow window = null)
+ {
+ throw new InvalidOperationException("This method is only intended to be used with SetWindowFields.");
+ }
+
+ ///
+ /// Returns the median of the numeric values. Median ignores non-numeric values.
+ ///
+ /// The type of the input documents in the partition.
+ /// The partition.
+ /// The selector that selects a value from the input document.
+ /// The window boundaries.
+ /// The median of the selected values.
+ public static double? Median(this ISetWindowFieldsPartition partition, Func selector, SetWindowFieldsWindow window = null)
+ {
+ throw new InvalidOperationException("This method is only intended to be used with SetWindowFields.");
+ }
+
+ ///
+ /// Returns the median of the numeric values. Median ignores non-numeric values.
+ ///
+ /// The type of the input documents in the partition.
+ /// The partition.
+ /// The selector that selects a value from the input document.
+ /// The window boundaries.
+ /// The median of the selected values.
+ public static float Median(this ISetWindowFieldsPartition partition, Func selector, SetWindowFieldsWindow window = null)
+ {
+ throw new InvalidOperationException("This method is only intended to be used with SetWindowFields.");
+ }
+
+ ///
+ /// Returns the median of the numeric values. Median ignores non-numeric values.
+ ///
+ /// The type of the input documents in the partition.
+ /// The partition.
+ /// The selector that selects a value from the input document.
+ /// The window boundaries.
+ /// The median of the selected values.
+ public static float? Median(this ISetWindowFieldsPartition partition, Func selector, SetWindowFieldsWindow window = null)
+ {
+ throw new InvalidOperationException("This method is only intended to be used with SetWindowFields.");
+ }
+
+ ///
+ /// Returns the median of the numeric values. Median ignores non-numeric values.
+ ///
+ /// The type of the input documents in the partition.
+ /// The partition.
+ /// The selector that selects a value from the input document.
+ /// The window boundaries.
+ /// The median of the selected values.
+ public static double Median(this ISetWindowFieldsPartition partition, Func selector, SetWindowFieldsWindow window = null)
+ {
+ throw new InvalidOperationException("This method is only intended to be used with SetWindowFields.");
+ }
+
+ ///
+ /// Returns the median of the numeric values. Median ignores non-numeric values.
+ ///
+ /// The type of the input documents in the partition.
+ /// The partition.
+ /// The selector that selects a value from the input document.
+ /// The window boundaries.
+ /// The median of the selected values.
+ public static double? Median(this ISetWindowFieldsPartition partition, Func selector, SetWindowFieldsWindow window = null)
+ {
+ throw new InvalidOperationException("This method is only intended to be used with SetWindowFields.");
+ }
+
+ ///
+ /// Returns the median of the numeric values. Median ignores non-numeric values.
+ ///
+ /// The type of the input documents in the partition.
+ /// The partition.
+ /// The selector that selects a value from the input document.
+ /// The window boundaries.
+ /// The median of the selected values.
+ public static double Median(this ISetWindowFieldsPartition partition, Func selector, SetWindowFieldsWindow window = null)
+ {
+ throw new InvalidOperationException("This method is only intended to be used with SetWindowFields.");
+ }
+
+ ///
+ /// Returns the median of the numeric values. Median ignores non-numeric values.
+ ///
+ /// The type of the input documents in the partition.
+ /// The partition.
+ /// The selector that selects a value from the input document.
+ /// The window boundaries.
+ /// The median of the selected values.
+ public static double? Median(this ISetWindowFieldsPartition partition, Func selector, SetWindowFieldsWindow window = null)
+ {
+ throw new InvalidOperationException("This method is only intended to be used with SetWindowFields.");
+ }
+
///
/// Returns the minimum value.
///
@@ -893,6 +1022,146 @@ public static TValue Min(this ISetWindowFieldsPartition
throw new InvalidOperationException("This method is only intended to be used with SetWindowFields.");
}
+ ///
+ /// Returns the values at the given percentiles. Percentile ignores non-numeric values.
+ ///
+ /// The type of the input documents in the partition.
+ /// The partition.
+ /// The selector that selects a value from the input document.
+ /// The percentiles (between 0.0 and 1.0).
+ /// The window boundaries.
+ /// The values at the given percentiles.
+ public static decimal[] Percentile(this ISetWindowFieldsPartition partition, Func selector, IEnumerable percentiles, SetWindowFieldsWindow window = null)
+ {
+ throw new InvalidOperationException("This method is only intended to be used with SetWindowFields.");
+ }
+
+ ///
+ /// Returns the values at the given percentiles. Percentile ignores non-numeric values. Percentile returns results in the same order as the given percentiles.
+ ///
+ /// The type of the input documents in the partition.
+ /// The partition.
+ /// The selector that selects a value from the input document.
+ /// The percentiles (between 0.0 and 1.0).
+ /// The window boundaries.
+ /// The values at the given percentiles.
+ public static decimal?[] Percentile(this ISetWindowFieldsPartition partition, Func selector, IEnumerable percentiles, SetWindowFieldsWindow window = null)
+ {
+ throw new InvalidOperationException("This method is only intended to be used with SetWindowFields.");
+ }
+
+ ///
+ /// Returns the values at the given percentiles. Percentile ignores non-numeric values. Percentile returns results in the same order as the given percentiles.
+ ///
+ /// The type of the input documents in the partition.
+ /// The partition.
+ /// The selector that selects a value from the input document.
+ /// The percentiles (between 0.0 and 1.0).
+ /// The window boundaries.
+ /// The values at the given percentiles.
+ public static double[] Percentile(this ISetWindowFieldsPartition partition, Func selector, IEnumerable percentiles, SetWindowFieldsWindow window = null)
+ {
+ throw new InvalidOperationException("This method is only intended to be used with SetWindowFields.");
+ }
+
+ ///
+ /// Returns the values at the given percentiles. Percentile ignores non-numeric values. Percentile returns results in the same order as the given percentiles.
+ ///
+ /// The type of the input documents in the partition.
+ /// The partition.
+ /// The selector that selects a value from the input document.
+ /// The percentiles (between 0.0 and 1.0).
+ /// The window boundaries.
+ /// The values at the given percentiles.
+ public static double?[] Percentile(this ISetWindowFieldsPartition partition, Func selector, IEnumerable percentiles, SetWindowFieldsWindow window = null)
+ {
+ throw new InvalidOperationException("This method is only intended to be used with SetWindowFields.");
+ }
+
+ ///
+ /// Returns the values at the given percentiles. Percentile ignores non-numeric values. Percentile returns results in the same order as the given percentiles.
+ ///
+ /// The type of the input documents in the partition.
+ /// The partition.
+ /// The selector that selects a value from the input document.
+ /// The percentiles (between 0.0 and 1.0).
+ /// The window boundaries.
+ /// The values at the given percentiles.
+ public static float[] Percentile(this ISetWindowFieldsPartition partition, Func selector, IEnumerable percentiles, SetWindowFieldsWindow window = null)
+ {
+ throw new InvalidOperationException("This method is only intended to be used with SetWindowFields.");
+ }
+
+ ///
+ /// Returns the values at the given percentiles. Percentile ignores non-numeric values. Percentile returns results in the same order as the given percentiles.
+ ///
+ /// The type of the input documents in the partition.
+ /// The partition.
+ /// The selector that selects a value from the input document.
+ /// The percentiles (between 0.0 and 1.0).
+ /// The window boundaries.
+ /// The values at the given percentiles.
+ public static float?[] Percentile(this ISetWindowFieldsPartition partition, Func selector, IEnumerable percentiles, SetWindowFieldsWindow window = null)
+ {
+ throw new InvalidOperationException("This method is only intended to be used with SetWindowFields.");
+ }
+
+ ///
+ /// Returns the values at the given percentiles. Percentile ignores non-numeric values. Percentile returns results in the same order as the given percentiles.
+ ///
+ /// The type of the input documents in the partition.
+ /// The partition.
+ /// The selector that selects a value from the input document.
+ /// The percentiles (between 0.0 and 1.0).
+ /// The window boundaries.
+ /// The values at the given percentiles.
+ public static double[] Percentile(this ISetWindowFieldsPartition partition, Func selector, IEnumerable percentiles, SetWindowFieldsWindow window = null)
+ {
+ throw new InvalidOperationException("This method is only intended to be used with SetWindowFields.");
+ }
+
+ ///
+ /// Returns the values at the given percentiles. Percentile ignores non-numeric values. Percentile returns results in the same order as the given percentiles.
+ ///
+ /// The type of the input documents in the partition.
+ /// The partition.
+ /// The selector that selects a value from the input document.
+ /// The percentiles (between 0.0 and 1.0).
+ /// The window boundaries.
+ /// The values at the given percentiles.
+ public static double?[] Percentile(this ISetWindowFieldsPartition partition, Func selector, IEnumerable percentiles, SetWindowFieldsWindow window = null)
+ {
+ throw new InvalidOperationException("This method is only intended to be used with SetWindowFields.");
+ }
+
+ ///
+ /// Returns the values at the given percentiles. Percentile ignores non-numeric values. Percentile returns results in the same order as the given percentiles.
+ ///
+ /// The type of the input documents in the partition.
+ /// The partition.
+ /// The selector that selects a value from the input document.
+ /// The percentiles (between 0.0 and 1.0).
+ /// The window boundaries.
+ /// The values at the given percentiles.
+ public static double[] Percentile(this ISetWindowFieldsPartition partition, Func selector, IEnumerable percentiles, SetWindowFieldsWindow window = null)
+ {
+ throw new InvalidOperationException("This method is only intended to be used with SetWindowFields.");
+ }
+
+ ///
+ /// Returns the values at the given percentiles. Percentile ignores non-numeric values. Percentile returns results in the same order as the given percentiles.
+ ///
+ /// The type of the input documents in the partition.
+ /// The partition.
+ /// The selector that selects a value from the input document.
+ /// The percentiles (between 0.0 and 1.0).
+ /// The window boundaries.
+ /// The values at the given percentiles.
+ public static double?[] Percentile(this ISetWindowFieldsPartition partition, Func selector, IEnumerable percentiles, SetWindowFieldsWindow window = null)
+ {
+ throw new InvalidOperationException("This method is only intended to be used with SetWindowFields.");
+ }
+
///
/// Returns a sequence of values.
///
diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/AstNodeType.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/AstNodeType.cs
index 2b8ce448dd3..147bd427729 100644
--- a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/AstNodeType.cs
+++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/AstNodeType.cs
@@ -93,6 +93,9 @@ internal enum AstNodeType
MatchesEverythingFilter,
MatchesNothingFilter,
MatchStage,
+ MedianExpression,
+ MedianAccumulatorExpression,
+ MedianWindowExpression,
MergeStage,
ModFilterOperation,
NaryExpression,
@@ -104,6 +107,9 @@ internal enum AstNodeType
NullaryWindowExpression,
OrFilter,
OutStage,
+ PercentileExpression,
+ PercentileAccumulatorExpression,
+ PercentileWindowExpression,
PickAccumulatorExpression,
PickExpression,
Pipeline,
diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstExpression.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstExpression.cs
index 90512554fc6..bf729df32a9 100644
--- a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstExpression.cs
+++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstExpression.cs
@@ -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 AstAccumulatorExpression MedianAccumulator(AstExpression input)
+ {
+ return new AstMedianAccumulatorExpression(input);
+ }
+
+ public static AstWindowExpression MedianWindowExpression(AstExpression input, AstWindow window)
+ {
+ return new AstMedianWindowExpression(input, window);
+ }
+
public static AstExpression Min(AstExpression array)
{
return new AstUnaryExpression(AstUnaryOperator.Min, array);
@@ -653,6 +668,21 @@ public static AstExpression Or(params AstExpression[] args)
return new AstNaryExpression(AstNaryOperator.Or, flattenedArgs);
}
+ public static AstExpression Percentile(AstExpression input, AstExpression percentiles)
+ {
+ return new AstPercentileExpression(input, percentiles);
+ }
+
+ public static AstAccumulatorExpression PercentileAccumulator(AstExpression input, AstExpression percentiles)
+ {
+ return new AstPercentileAccumulatorExpression(input, percentiles);
+ }
+
+ public static AstWindowExpression 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);
diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstMedianAccumulatorExpression.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstMedianAccumulatorExpression.cs
new file mode 100644
index 00000000000..d7cc2bb085a
--- /dev/null
+++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstMedianAccumulatorExpression.cs
@@ -0,0 +1,63 @@
+/* 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);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstMedianExpression.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstMedianExpression.cs
new file mode 100644
index 00000000000..a171bb9fa02
--- /dev/null
+++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstMedianExpression.cs
@@ -0,0 +1,63 @@
+/* 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);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstMedianWindowExpression.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstMedianWindowExpression.cs
new file mode 100644
index 00000000000..fbe71c3f278
--- /dev/null
+++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstMedianWindowExpression.cs
@@ -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);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstPercentileAccumulatorExpression.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstPercentileAccumulatorExpression.cs
new file mode 100644
index 00000000000..77cee275402
--- /dev/null
+++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstPercentileAccumulatorExpression.cs
@@ -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);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstPercentileExpression.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstPercentileExpression.cs
new file mode 100644
index 00000000000..aab4920143d
--- /dev/null
+++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstPercentileExpression.cs
@@ -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 AstPercentileExpression : AstExpression
+ {
+ private readonly AstExpression _input;
+ private readonly AstExpression _percentiles;
+
+ public AstPercentileExpression(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.PercentileExpression;
+
+ public override AstNode Accept(AstNodeVisitor visitor)
+ {
+ return visitor.VisitPercentileExpression(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 AstPercentileExpression Update(AstExpression input, AstExpression percentiles)
+ {
+ if (input == _input && percentiles == _percentiles)
+ {
+ return this;
+ }
+ return new AstPercentileExpression(input, percentiles);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstPercentileWindowExpression.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstPercentileWindowExpression.cs
new file mode 100644
index 00000000000..055bcfb8ef3
--- /dev/null
+++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstPercentileWindowExpression.cs
@@ -0,0 +1,74 @@
+/* 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 AstPercentileWindowExpression : AstWindowExpression
+ {
+ private readonly AstExpression _input;
+ private readonly AstExpression _percentiles;
+ private readonly AstWindow _window;
+
+ public AstPercentileWindowExpression(AstExpression input, AstExpression percentiles, AstWindow window)
+ {
+ _input = Ensure.IsNotNull(input, nameof(input));
+ _percentiles = Ensure.IsNotNull(percentiles, nameof(percentiles));
+ _window = window;
+ }
+
+ public AstExpression Input => _input;
+
+ public AstExpression Percentiles => _percentiles;
+
+ public AstWindow Window => _window;
+
+ public override AstNodeType NodeType => AstNodeType.PercentileWindowExpression;
+
+ public override AstNode Accept(AstNodeVisitor visitor)
+ {
+ return visitor.VisitPercentileWindowExpression(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
+ }
+ },
+ { "window", _window?.Render(), _window != null }
+ };
+ }
+
+ public AstPercentileWindowExpression Update(AstExpression input, AstExpression percentiles, AstWindow window)
+ {
+ if (input == _input && percentiles == _percentiles && window == _window)
+ {
+ return this;
+ }
+
+ return new AstPercentileWindowExpression(input, percentiles, window);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Optimizers/AstGroupingPipelineOptimizer.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Optimizers/AstGroupingPipelineOptimizer.cs
index 5967215ee25..37de0673850 100644
--- a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Optimizers/AstGroupingPipelineOptimizer.cs
+++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Optimizers/AstGroupingPipelineOptimizer.cs
@@ -404,32 +404,67 @@ unaryExpression.Arg is AstGetFieldExpression innerMostGetFieldExpression &&
public override AstNode VisitMapExpression(AstMapExpression node)
{
// { $map : { input : { $getField : { input : "$$ROOT", field : "_elements" } }, as : "x", in : f(x) } } => { __agg0 : { $push : f(x => element) } } + "$__agg0"
- if (node.Input is AstGetFieldExpression mapInputGetFieldExpression &&
- mapInputGetFieldExpression.FieldName.IsStringConstant("_elements") &&
- mapInputGetFieldExpression.Input.IsRootVar())
+ if (IsMappedElementsField(node, out var rewrittenArg))
{
- var rewrittenArg = (AstExpression)AstNodeReplacer.Replace(node.In, (node.As, _element));
var accumulatorExpression = AstExpression.UnaryAccumulator(AstUnaryAccumulatorOperator.Push, rewrittenArg);
- var accumulatorFieldName = _accumulators.AddAccumulatorExpression(accumulatorExpression);
- return AstExpression.GetField(AstExpression.RootVar, accumulatorFieldName);
+ return CreateGetAccumulatorFieldExpression(accumulatorExpression);
}
return base.VisitMapExpression(node);
}
+ public override AstNode VisitMedianExpression(AstMedianExpression node)
+ {
+ // { $median : { input: { $getField : { input : "$$ROOT", field : "_elements" } }, method: "approximate" } }
+ // => { __agg0 : { $median : { input: element, method: "approximate" } } } + "$__agg0"
+ if (IsElementsField(node.Input))
+ {
+ var accumulatorExpression = AstExpression.MedianAccumulator(_element);
+ return CreateGetAccumulatorFieldExpression(accumulatorExpression);
+ }
+
+ // { $median : { input: { $map : { input : { $getField : { input : "$$ROOT", field : "_elements" } }, as : "x", in : f(x) } }, method: "approximate" } }
+ // => { __agg0 : { $median : { input: f(x => element), method: "approximate" } } } + "$__agg0"
+ if (IsMappedElementsField(node.Input, out var rewrittenArg))
+ {
+ var accumulatorExpression = AstExpression.MedianAccumulator(rewrittenArg);
+ return CreateGetAccumulatorFieldExpression(accumulatorExpression);
+ }
+
+ return base.VisitMedianExpression(node);
+ }
+
+ public override AstNode VisitPercentileExpression(AstPercentileExpression node)
+ {
+ // { $percentile : { input: { $getField : { input : "$$ROOT", field : "_elements" } }, p: [...], method: "approximate" } }
+ // => { __agg0 : { $percentile : { input: element, p: [...], method: "approximate" } } } + "$__agg0"
+ if (IsElementsField(node.Input))
+ {
+ var accumulatorExpression = AstExpression.PercentileAccumulator(_element, node.Percentiles);
+ return CreateGetAccumulatorFieldExpression(accumulatorExpression);
+ }
+
+ // { $percentile : { input: { $map : { input : { $getField : { input : "$$ROOT", field : "_elements" } }, as : "x", in : f(x) } }, p: [...], method: "approximate" } }
+ // => { __agg0 : { $percentile : { input: f(x => element), p: [...], method: "approximate" } } } + "$__agg0"
+ if (IsMappedElementsField(node.Input, out var rewrittenArg))
+ {
+ var accumulatorExpression = AstExpression.PercentileAccumulator(rewrittenArg, node.Percentiles);
+ return CreateGetAccumulatorFieldExpression(accumulatorExpression);
+ }
+
+ return base.VisitPercentileExpression(node);
+ }
+
public override AstNode VisitPickExpression(AstPickExpression node)
{
// { $pickOperator : { source : { $getField : { input : "$$ROOT", field : "_elements" } }, as : "x", sortBy : s, selector : f(x) } }
// => { __agg0 : { $pickAccumulatorOperator : { sortBy : s, selector : f(x => element) } } } + "$__agg0"
- if (node.Source is AstGetFieldExpression getFieldExpression &&
- getFieldExpression.Input.IsRootVar() &&
- getFieldExpression.FieldName.IsStringConstant("_elements"))
+ if (IsElementsField(node.Source))
{
var @operator = node.Operator.ToAccumulatorOperator();
var rewrittenSelector = (AstExpression)AstNodeReplacer.Replace(node.Selector, (node.As, _element));
var accumulatorExpression = new AstPickAccumulatorExpression(@operator, node.SortBy, rewrittenSelector, node.N);
- var accumulatorFieldName = _accumulators.AddAccumulatorExpression(accumulatorExpression);
- return AstExpression.GetField(AstExpression.RootVar, accumulatorFieldName);
+ return CreateGetAccumulatorFieldExpression(accumulatorExpression);
}
return base.VisitPickExpression(node);
@@ -437,80 +472,60 @@ public override AstNode VisitPickExpression(AstPickExpression node)
public override AstNode VisitUnaryExpression(AstUnaryExpression node)
{
- if (TryOptimizeSizeOfElements(out var optimizedExpression))
+ // { $size : "$_elements" } => { __agg0 : { $sum : 1 } } + "$__agg0"
+ if (node.Operator == AstUnaryOperator.Size)
{
- return optimizedExpression;
+ if (node.Arg is AstGetFieldExpression argGetFieldExpression &&
+ argGetFieldExpression.FieldName.IsStringConstant("_elements"))
+ {
+ var accumulatorExpression = AstExpression.UnaryAccumulator(AstUnaryAccumulatorOperator.Sum, 1);
+ return CreateGetAccumulatorFieldExpression(accumulatorExpression);
+ }
}
- if (TryOptimizeAccumulatorOfElements(out optimizedExpression))
+ // { $accumulator : { $getField : { input : "$$ROOT", field : "_elements" } } } => { __agg0 : { $accumulator : element } } + "$__agg0"
+ if (node.Operator.IsAccumulator(out var accumulatorOperator) && IsElementsField(node.Arg))
{
- return optimizedExpression;
+ var accumulatorExpression = AstExpression.UnaryAccumulator(accumulatorOperator, _element);
+ return CreateGetAccumulatorFieldExpression(accumulatorExpression);
}
- if (TryOptimizeAccumulatorOfMappedElements(out optimizedExpression))
+ // { $accumulator : { $map : { input : { $getField : { input : "$$ROOT", field : "_elements" } }, as : "x", in : f(x) } } }
+ // => { __agg0 : { $accumulator : f(x => element) } } + "$__agg0"
+ if (node.Operator.IsAccumulator(out accumulatorOperator) &&
+ IsMappedElementsField(node.Arg, out var rewrittenArg))
{
- return optimizedExpression;
+ var accumulatorExpression = AstExpression.UnaryAccumulator(accumulatorOperator, rewrittenArg);
+ return CreateGetAccumulatorFieldExpression(accumulatorExpression);
}
return base.VisitUnaryExpression(node);
+ }
- bool TryOptimizeSizeOfElements(out AstExpression optimizedExpression)
- {
- // { $size : "$_elements" } => { __agg0 : { $sum : 1 } } + "$__agg0"
- if (node.Operator == AstUnaryOperator.Size)
- {
- if (node.Arg is AstGetFieldExpression argGetFieldExpression &&
- argGetFieldExpression.FieldName.IsStringConstant("_elements"))
- {
- var accumulatorExpression = AstExpression.UnaryAccumulator(AstUnaryAccumulatorOperator.Sum, 1);
- var accumulatorFieldName = _accumulators.AddAccumulatorExpression(accumulatorExpression);
- optimizedExpression = AstExpression.GetField(AstExpression.RootVar, accumulatorFieldName);
- return true;
- }
- }
-
- optimizedExpression = null;
- return false;
- }
+ private bool IsElementsField(AstExpression expression)
+ {
+ return
+ expression is AstGetFieldExpression getFieldExpression &&
+ getFieldExpression.FieldName.IsStringConstant("_elements") &&
+ getFieldExpression.Input.IsRootVar();
+ }
- bool TryOptimizeAccumulatorOfElements(out AstExpression optimizedExpression)
+ private bool IsMappedElementsField(AstExpression expression, out AstExpression rewrittenArg)
+ {
+ if (expression is AstMapExpression mapExpression && IsElementsField(mapExpression.Input))
{
- // { $accumulator : { $getField : { input : "$$ROOT", field : "_elements" } } } => { __agg0 : { $accumulator : element } } + "$__agg0"
- if (node.Operator.IsAccumulator(out var accumulatorOperator) &&
- node.Arg is AstGetFieldExpression getFieldExpression &&
- getFieldExpression.FieldName.IsStringConstant("_elements") &&
- getFieldExpression.Input.IsRootVar())
- {
- var accumulatorExpression = AstExpression.UnaryAccumulator(accumulatorOperator, _element);
- var accumulatorFieldName = _accumulators.AddAccumulatorExpression(accumulatorExpression);
- optimizedExpression = AstExpression.GetField(AstExpression.RootVar, accumulatorFieldName);
- return true;
- }
-
- optimizedExpression = null;
- return false;
-
+ rewrittenArg = (AstExpression)AstNodeReplacer.Replace(mapExpression.In, (mapExpression.As, _element));
+ return true;
}
- bool TryOptimizeAccumulatorOfMappedElements(out AstExpression optimizedExpression)
- {
- // { $accumulator : { $map : { input : { $getField : { input : "$$ROOT", field : "_elements" } }, as : "x", in : f(x) } } } => { __agg0 : { $accumulator : f(x => element) } } + "$__agg0"
- if (node.Operator.IsAccumulator(out var accumulatorOperator) &&
- node.Arg is AstMapExpression mapExpression &&
- mapExpression.Input is AstGetFieldExpression mapInputGetFieldExpression &&
- mapInputGetFieldExpression.FieldName.IsStringConstant("_elements") &&
- mapInputGetFieldExpression.Input.IsRootVar())
- {
- var rewrittenArg = (AstExpression)AstNodeReplacer.Replace(mapExpression.In, (mapExpression.As, _element));
- var accumulatorExpression = AstExpression.UnaryAccumulator(accumulatorOperator, rewrittenArg);
- var accumulatorFieldName = _accumulators.AddAccumulatorExpression(accumulatorExpression);
- optimizedExpression = AstExpression.GetField(AstExpression.RootVar, accumulatorFieldName);
- return true;
- }
+ rewrittenArg = null;
+ return false;
+ }
- optimizedExpression = null;
- return false;
- }
+ private AstExpression CreateGetAccumulatorFieldExpression(AstAccumulatorExpression accumulatorExpression)
+ {
+ var fieldName = _accumulators.AddAccumulatorExpression(accumulatorExpression);
+ return AstExpression.GetField(AstExpression.RootVar, fieldName);
}
}
diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Visitors/AstNodeVisitor.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Visitors/AstNodeVisitor.cs
index 1222d0060e9..83a50bd6f44 100644
--- a/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Visitors/AstNodeVisitor.cs
+++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Visitors/AstNodeVisitor.cs
@@ -504,6 +504,21 @@ public virtual AstNode VisitMatchStage(AstMatchStage node)
return node.Update(VisitAndConvert(node.Filter));
}
+ public virtual AstNode VisitMedianExpression(AstMedianExpression node)
+ {
+ return node.Update(VisitAndConvert(node.Input));
+ }
+
+ public virtual AstNode VisitMedianAccumulatorExpression(AstMedianAccumulatorExpression node)
+ {
+ return node.Update(VisitAndConvert(node.Input));
+ }
+
+ public virtual AstNode VisitMedianWindowExpression(AstMedianWindowExpression node)
+ {
+ return node.Update(VisitAndConvert(node.Input), node.Window);
+ }
+
public virtual AstNode VisitMergeStage(AstMergeStage node)
{
return node.Update(VisitAndConvert(node.Let));
@@ -559,6 +574,21 @@ public virtual AstNode VisitOutStage(AstOutStage node)
return node;
}
+ public virtual AstNode VisitPercentileExpression(AstPercentileExpression node)
+ {
+ return node.Update(VisitAndConvert(node.Input), VisitAndConvert(node.Percentiles));
+ }
+
+ public virtual AstNode VisitPercentileAccumulatorExpression(AstPercentileAccumulatorExpression node)
+ {
+ return node.Update(VisitAndConvert(node.Input), VisitAndConvert(node.Percentiles));
+ }
+
+ public virtual AstNode VisitPercentileWindowExpression(AstPercentileWindowExpression node)
+ {
+ return node.Update(VisitAndConvert(node.Input), VisitAndConvert(node.Percentiles), node.Window);
+ }
+
public virtual AstNode VisitPickAccumulatorExpression(AstPickAccumulatorExpression node)
{
return node.Update(node.Operator, node.SortBy, VisitAndConvert(node.Selector), VisitAndConvert(node.N));
diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/MongoEnumerableMethod.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/MongoEnumerableMethod.cs
index 3773f1f92bf..c10550024c3 100644
--- a/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/MongoEnumerableMethod.cs
+++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Reflection/MongoEnumerableMethod.cs
@@ -25,6 +25,46 @@ internal static class MongoEnumerableMethod
private static readonly MethodInfo __allElements;
private static readonly MethodInfo __allMatchingElements;
private static readonly MethodInfo __firstMatchingElement;
+ private static readonly MethodInfo __medianDecimal;
+ private static readonly MethodInfo __medianDecimalWithSelector;
+ private static readonly MethodInfo __medianDouble;
+ private static readonly MethodInfo __medianDoubleWithSelector;
+ private static readonly MethodInfo __medianInt32;
+ private static readonly MethodInfo __medianInt32WithSelector;
+ private static readonly MethodInfo __medianInt64;
+ private static readonly MethodInfo __medianInt64WithSelector;
+ private static readonly MethodInfo __medianNullableDecimal;
+ private static readonly MethodInfo __medianNullableDecimalWithSelector;
+ private static readonly MethodInfo __medianNullableDouble;
+ private static readonly MethodInfo __medianNullableDoubleWithSelector;
+ private static readonly MethodInfo __medianNullableInt32;
+ private static readonly MethodInfo __medianNullableInt32WithSelector;
+ private static readonly MethodInfo __medianNullableInt64;
+ private static readonly MethodInfo __medianNullableInt64WithSelector;
+ private static readonly MethodInfo __medianNullableSingle;
+ private static readonly MethodInfo __medianNullableSingleWithSelector;
+ private static readonly MethodInfo __medianSingle;
+ private static readonly MethodInfo __medianSingleWithSelector;
+ private static readonly MethodInfo __percentileDecimal;
+ private static readonly MethodInfo __percentileDecimalWithSelector;
+ private static readonly MethodInfo __percentileDouble;
+ private static readonly MethodInfo __percentileDoubleWithSelector;
+ private static readonly MethodInfo __percentileInt32;
+ private static readonly MethodInfo __percentileInt32WithSelector;
+ private static readonly MethodInfo __percentileInt64;
+ private static readonly MethodInfo __percentileInt64WithSelector;
+ private static readonly MethodInfo __percentileNullableDecimal;
+ private static readonly MethodInfo __percentileNullableDecimalWithSelector;
+ private static readonly MethodInfo __percentileNullableDouble;
+ private static readonly MethodInfo __percentileNullableDoubleWithSelector;
+ private static readonly MethodInfo __percentileNullableInt32;
+ private static readonly MethodInfo __percentileNullableInt32WithSelector;
+ private static readonly MethodInfo __percentileNullableInt64;
+ private static readonly MethodInfo __percentileNullableInt64WithSelector;
+ private static readonly MethodInfo __percentileNullableSingle;
+ private static readonly MethodInfo __percentileNullableSingleWithSelector;
+ private static readonly MethodInfo __percentileSingle;
+ private static readonly MethodInfo __percentileSingleWithSelector;
private static readonly MethodInfo __whereWithLimit;
// static constructor
@@ -33,6 +73,46 @@ static MongoEnumerableMethod()
__allElements = ReflectionInfo.Method((IEnumerable