diff --git a/generator/config/accumulator/covariancePop.yaml b/generator/config/accumulator/covariancePop.yaml index 758b90b..5b41e6a 100644 --- a/generator/config/accumulator/covariancePop.yaml +++ b/generator/config/accumulator/covariancePop.yaml @@ -16,3 +16,25 @@ arguments: name: expression2 type: - resolvesToNumber +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/covariancePop/#example' + pipeline: + - + $setWindowFields: + partitionBy: '$state' + sortBy: + orderDate: 1 + output: + covariancePopForState: + $covariancePop: + - + # $year: '$orderDate' + $year: + date: '$orderDate' + - '$quantity' + window: + documents: + - 'unbounded' + - 'current' diff --git a/generator/config/accumulator/covarianceSamp.yaml b/generator/config/accumulator/covarianceSamp.yaml index 072c5a7..0c3bb86 100644 --- a/generator/config/accumulator/covarianceSamp.yaml +++ b/generator/config/accumulator/covarianceSamp.yaml @@ -16,3 +16,25 @@ arguments: name: expression2 type: - resolvesToNumber +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/covarianceSamp/#example' + pipeline: + - + $setWindowFields: + partitionBy: '$state' + sortBy: + orderDate: 1 + output: + covarianceSampForState: + $covarianceSamp: + - + # $year: '$orderDate' + $year: + date: '$orderDate' + - '$quantity' + window: + documents: + - 'unbounded' + - 'current' diff --git a/generator/config/accumulator/denseRank.yaml b/generator/config/accumulator/denseRank.yaml index 2a587cc..0c50dd9 100644 --- a/generator/config/accumulator/denseRank.yaml +++ b/generator/config/accumulator/denseRank.yaml @@ -7,3 +7,28 @@ encode: object description: | Returns the document position (known as the rank) relative to other documents in the $setWindowFields stage partition. There are no gaps in the ranks. Ties receive the same rank. New in MongoDB 5.0. +tests: + - + name: 'Dense Rank Partitions by an Integer Field' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/denseRank/#dense-rank-partitions-by-an-integer-field' + pipeline: + - + $setWindowFields: + partitionBy: '$state' + sortBy: + quantity: -1 + output: + denseRankQuantityForState: + $denseRank: {} + - + name: 'Dense Rank Partitions by a Date Field' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/denseRank/#dense-rank-partitions-by-a-date-field' + pipeline: + - + $setWindowFields: + partitionBy: '$state' + sortBy: + orderDate: 1 + output: + denseRankOrderDateForState: + $denseRank: {} diff --git a/generator/config/accumulator/derivative.yaml b/generator/config/accumulator/derivative.yaml index 974b4d6..90c2ee0 100644 --- a/generator/config/accumulator/derivative.yaml +++ b/generator/config/accumulator/derivative.yaml @@ -21,3 +21,27 @@ arguments: description: | A string that specifies the time unit. Use one of these strings: "week", "day","hour", "minute", "second", "millisecond". If the sortBy field is not a date, you must omit a unit. If you specify a unit, you must specify a date in the sortBy field. +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/derivative/#example' + pipeline: + - + $setWindowFields: + partitionBy: '$truckID' + sortBy: + timeStamp: 1 + output: + truckAverageSpeed: + $derivative: + input: '$miles' + unit: 'hour' + window: + range: + - -30 + - 0 + unit: 'second' + - + $match: + truckAverageSpeed: + $gt: 50 diff --git a/generator/config/accumulator/documentNumber.yaml b/generator/config/accumulator/documentNumber.yaml index 3ae8c8c..b810ccd 100644 --- a/generator/config/accumulator/documentNumber.yaml +++ b/generator/config/accumulator/documentNumber.yaml @@ -7,3 +7,16 @@ encode: object description: | Returns the position of a document (known as the document number) in the $setWindowFields stage partition. Ties result in different adjacent document numbers. New in MongoDB 5.0. +tests: + - + name: 'Document Number for Each State' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/documentNumber/#document-number-for-each-state' + pipeline: + - + $setWindowFields: + partitionBy: '$state' + sortBy: + quantity: -1 + output: + documentNumberForState: + $documentNumber: {} diff --git a/generator/config/accumulator/expMovingAvg.yaml b/generator/config/accumulator/expMovingAvg.yaml index 981cde4..3009dd1 100644 --- a/generator/config/accumulator/expMovingAvg.yaml +++ b/generator/config/accumulator/expMovingAvg.yaml @@ -29,3 +29,32 @@ arguments: description: | A double that specifies the exponential decay value to use in the exponential moving average calculation. A higher alpha value assigns a lower mathematical significance to previous results from the calculation. You must specify either N or alpha. You cannot specify both. +tests: + - + name: 'Exponential Moving Average Using N' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/expMovingAvg/#exponential-moving-average-using-n' + pipeline: + - + $setWindowFields: + partitionBy: '$stock' + sortBy: + date: 1 + output: + expMovingAvgForStock: + $expMovingAvg: + input: '$price' + N: 2 + - + name: 'Exponential Moving Average Using alpha' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/expMovingAvg/#exponential-moving-average-using-alpha' + pipeline: + - + $setWindowFields: + partitionBy: '$stock' + sortBy: + date: 1 + output: + expMovingAvgForStock: + $expMovingAvg: + input: '$price' + alpha: 0.75 diff --git a/generator/config/accumulator/integral.yaml b/generator/config/accumulator/integral.yaml new file mode 100644 index 0000000..6c84548 --- /dev/null +++ b/generator/config/accumulator/integral.yaml @@ -0,0 +1,43 @@ +# $schema: ../schema.json +name: $integral +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/integral/' +type: + - window +encode: object +description: | + Returns the approximation of the area under a curve. + New in MongoDB 5.0. +arguments: + - + name: input + type: + - resolvesToNumber + - resolvesToDate + - + name: unit + type: + - resolvesToString + optional: true + description: | + A string that specifies the time unit. Use one of these strings: "week", "day","hour", "minute", "second", "millisecond". + If the sortBy field is not a date, you must omit a unit. If you specify a unit, you must specify a date in the sortBy field. +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/integral/#example' + pipeline: + - + $setWindowFields: + partitionBy: '$powerMeterID' + sortBy: + timeStamp: 1 + output: + powerMeterKilowattHours: + $integral: + input: '$kilowatts' + unit: 'hour' + window: + range: + - 'unbounded' + - 'current' + unit: 'hour' diff --git a/generator/config/accumulator/linearFill.yaml b/generator/config/accumulator/linearFill.yaml new file mode 100644 index 0000000..034e6ab --- /dev/null +++ b/generator/config/accumulator/linearFill.yaml @@ -0,0 +1,40 @@ +# $schema: ../schema.json +name: $linearFill +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/linearFill/' +type: + - window +encode: single +description: | + Fills null and missing fields in a window using linear interpolation based on surrounding field values. + Available in the $setWindowFields stage. + New in MongoDB 5.3. +arguments: + - + name: expression + type: + - resolvesToNumber +tests: + - + name: 'Fill Missing Values with Linear Interpolation' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/linearFill/#fill-missing-values-with-linear-interpolation' + pipeline: + - + $setWindowFields: + sortBy: + time: 1 + output: + price: + $linearFill: '$price' + - + name: 'Use Multiple Fill Methods in a Single Stage' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/linearFill/#use-multiple-fill-methods-in-a-single-stage' + pipeline: + - + $setWindowFields: + sortBy: + time: 1 + output: + linearFillPrice: + $linearFill: '$price' + locfPrice: + $locf: '$price' diff --git a/generator/config/accumulator/locf.yaml b/generator/config/accumulator/locf.yaml new file mode 100644 index 0000000..63979bc --- /dev/null +++ b/generator/config/accumulator/locf.yaml @@ -0,0 +1,27 @@ +# $schema: ../schema.json +name: $locf +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/locf/' +type: + - window +encode: single +description: | + Last observation carried forward. Sets values for null and missing fields in a window to the last non-null value for the field. + Available in the $setWindowFields stage. + New in MongoDB 5.2. +arguments: + - + name: expression + type: + - expression +tests: + - + name: 'Fill Missing Values with the Last Observed Value' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/locf/#fill-missing-values-with-the-last-observed-value' + pipeline: + - + $setWindowFields: + sortBy: + time: 1 + output: + price: + $locf: '$price' diff --git a/generator/config/accumulator/minN.yaml b/generator/config/accumulator/minN.yaml index 50737b0..24719a2 100644 --- a/generator/config/accumulator/minN.yaml +++ b/generator/config/accumulator/minN.yaml @@ -20,3 +20,54 @@ arguments: - resolvesToInt description: | An expression that resolves to a positive integer. The integer specifies the number of array elements that $maxN returns. +tests: + - + name: 'Find the Minimum Three Scores for a Single Game' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/minN/#find-the-minimum-three-scores-for-a-single-game' + pipeline: + - + $match: + gameId: 'G1' + - + $group: + _id: '$gameId' + minScores: + $minN: + input: + - '$score' + - '$playerId' + n: 3 + - + name: 'Finding the Minimum Three Documents Across Multiple Games' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/minN/#finding-the-minimum-three-documents-across-multiple-games' + pipeline: + - + $group: + _id: '$gameId' + minScores: + $minN: + input: + - '$score' + - '$playerId' + n: 3 + - + name: 'Computing n Based on the Group Key for $group' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/minN/#computing-n-based-on-the-group-key-for--group' + pipeline: + - + $group: + _id: + gameId: '$gameId' + gamescores: + $minN: + input: + - '$score' + - '$playerId' + n: + $cond: + if: + $eq: + - '$gameId' + - 'G2' + then: 1 + else: 3 diff --git a/generator/config/accumulator/rank.yaml b/generator/config/accumulator/rank.yaml new file mode 100644 index 0000000..8b8fd04 --- /dev/null +++ b/generator/config/accumulator/rank.yaml @@ -0,0 +1,34 @@ +# $schema: ../schema.json +name: $rank +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/rank/' +type: + - window +encode: object +description: | + Returns the document position (known as the rank) relative to other documents in the $setWindowFields stage partition. + New in MongoDB 5.0. +tests: + - + name: 'Rank Partitions by an Integer Field' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/rank/#rank-partitions-by-an-integer-field' + pipeline: + - + $setWindowFields: + partitionBy: '$state' + sortBy: + quantity: -1 + output: + rankQuantityForState: + $rank: {} + - + name: 'Rank Partitions by a Date Field' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/rank/#rank-partitions-by-a-date-field' + pipeline: + - + $setWindowFields: + partitionBy: '$state' + sortBy: + orderDate: 1 + output: + rankOrderDateForState: + $rank: {} diff --git a/generator/config/accumulator/shift.yaml b/generator/config/accumulator/shift.yaml index 38bc363..f4984f0 100644 --- a/generator/config/accumulator/shift.yaml +++ b/generator/config/accumulator/shift.yaml @@ -32,3 +32,34 @@ arguments: Specifies an optional default expression to evaluate if the document position is outside of the implicit $setWindowFields stage window. The implicit window contains all the documents in the partition. The default expression must evaluate to a constant value. If you do not specify a default expression, $shift returns null for documents whose positions are outside of the implicit $setWindowFields stage window. +tests: + - + name: 'Shift Using a Positive Integer' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/shift/#shift-using-a-positive-integer' + pipeline: + - + $setWindowFields: + partitionBy: '$state' + sortBy: + quantity: -1 + output: + shiftQuantityForState: + $shift: + output: '$quantity' + by: 1 + default: 'Not available' + - + name: 'Shift Using a Negative Integer' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/shift/#shift-using-a-negative-integer' + pipeline: + - + $setWindowFields: + partitionBy: '$state' + sortBy: + quantity: -1 + output: + shiftQuantityForState: + $shift: + output: '$quantity' + by: -1 + default: 'Not available' diff --git a/generator/config/expression/integral.yaml b/generator/config/expression/integral.yaml deleted file mode 100644 index 55228ea..0000000 --- a/generator/config/expression/integral.yaml +++ /dev/null @@ -1,24 +0,0 @@ -# $schema: ../schema.json -name: $integral -link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/integral/' -type: - - resolvesToDouble - - resolvesToDecimal -encode: object -description: | - Returns the approximation of the area under a curve. - New in MongoDB 5.0. -arguments: - - - name: input - type: - - resolvesToNumber - - resolvesToDate - - - name: unit - type: - - resolvesToString - optional: true - description: | - A string that specifies the time unit. Use one of these strings: "week", "day","hour", "minute", "second", "millisecond". - If the sortBy field is not a date, you must omit a unit. If you specify a unit, you must specify a date in the sortBy field. diff --git a/generator/config/expression/linearFill.yaml b/generator/config/expression/linearFill.yaml deleted file mode 100644 index a53ebe5..0000000 --- a/generator/config/expression/linearFill.yaml +++ /dev/null @@ -1,15 +0,0 @@ -# $schema: ../schema.json -name: $linearFill -link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/linearFill/' -type: - - resolvesToNumber -encode: single -description: | - Fills null and missing fields in a window using linear interpolation based on surrounding field values. - Available in the $setWindowFields stage. - New in MongoDB 5.3. -arguments: - - - name: expression - type: - - resolvesToNumber diff --git a/generator/config/expression/locf.yaml b/generator/config/expression/locf.yaml deleted file mode 100644 index 5e07962..0000000 --- a/generator/config/expression/locf.yaml +++ /dev/null @@ -1,15 +0,0 @@ -# $schema: ../schema.json -name: $locf -link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/locf/' -type: - - resolvesToAny -encode: single -description: | - Last observation carried forward. Sets values for null and missing fields in a window to the last non-null value for the field. - Available in the $setWindowFields stage. - New in MongoDB 5.2. -arguments: - - - name: expression - type: - - expression diff --git a/generator/config/expression/rank.yaml b/generator/config/expression/rank.yaml deleted file mode 100644 index dde4204..0000000 --- a/generator/config/expression/rank.yaml +++ /dev/null @@ -1,9 +0,0 @@ -# $schema: ../schema.json -name: $rank -link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/rank/' -type: - - resolvesToInt -encode: object -description: | - Returns the document position (known as the rank) relative to other documents in the $setWindowFields stage partition. - New in MongoDB 5.0. diff --git a/src/Builder/Accumulator/FactoryTrait.php b/src/Builder/Accumulator/FactoryTrait.php index 604e3c3..4b889da 100644 --- a/src/Builder/Accumulator/FactoryTrait.php +++ b/src/Builder/Accumulator/FactoryTrait.php @@ -21,6 +21,7 @@ use MongoDB\Builder\Expression\ResolvesToInt; use MongoDB\Builder\Expression\ResolvesToNumber; use MongoDB\Builder\Expression\ResolvesToObject; +use MongoDB\Builder\Expression\ResolvesToString; use MongoDB\Builder\Type\ExpressionInterface; use MongoDB\Builder\Type\Optional; use MongoDB\Model\BSONArray; @@ -253,6 +254,23 @@ public static function firstN( return new FirstNAccumulator($input, $n); } + /** + * Returns the approximation of the area under a curve. + * New in MongoDB 5.0. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/integral/ + * @param Decimal128|Int64|ResolvesToDate|ResolvesToNumber|UTCDateTime|float|int $input + * @param Optional|ResolvesToString|string $unit A string that specifies the time unit. Use one of these strings: "week", "day","hour", "minute", "second", "millisecond". + * If the sortBy field is not a date, you must omit a unit. If you specify a unit, you must specify a date in the sortBy field. + */ + public static function integral( + Decimal128|Int64|UTCDateTime|ResolvesToDate|ResolvesToNumber|float|int $input, + Optional|ResolvesToString|string $unit = Optional::Undefined, + ): IntegralAccumulator + { + return new IntegralAccumulator($input, $unit); + } + /** * Returns the result of an expression for the last document in a group or window. * Changed in MongoDB 5.0: Available in the $setWindowFields stage. @@ -284,6 +302,34 @@ public static function lastN( return new LastNAccumulator($input, $n); } + /** + * Fills null and missing fields in a window using linear interpolation based on surrounding field values. + * Available in the $setWindowFields stage. + * New in MongoDB 5.3. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/linearFill/ + * @param Decimal128|Int64|ResolvesToNumber|float|int $expression + */ + public static function linearFill(Decimal128|Int64|ResolvesToNumber|float|int $expression): LinearFillAccumulator + { + return new LinearFillAccumulator($expression); + } + + /** + * Last observation carried forward. Sets values for null and missing fields in a window to the last non-null value for the field. + * Available in the $setWindowFields stage. + * New in MongoDB 5.2. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/locf/ + * @param ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression + */ + public static function locf( + Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression, + ): LocfAccumulator + { + return new LocfAccumulator($expression); + } + /** * Returns the maximum value that results from applying an expression to each document. * Changed in MongoDB 5.0: Available in the $setWindowFields stage. @@ -415,6 +461,17 @@ public static function push( return new PushAccumulator($expression); } + /** + * Returns the document position (known as the rank) relative to other documents in the $setWindowFields stage partition. + * New in MongoDB 5.0. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/rank/ + */ + public static function rank(): RankAccumulator + { + return new RankAccumulator(); + } + /** * Returns the value from an expression applied to a document in a specified position relative to the current document in the $setWindowFields stage partition. * New in MongoDB 5.0. diff --git a/src/Builder/Expression/IntegralOperator.php b/src/Builder/Accumulator/IntegralAccumulator.php similarity index 86% rename from src/Builder/Expression/IntegralOperator.php rename to src/Builder/Accumulator/IntegralAccumulator.php index 2f2c6ef..9f99211 100644 --- a/src/Builder/Expression/IntegralOperator.php +++ b/src/Builder/Accumulator/IntegralAccumulator.php @@ -6,14 +6,18 @@ declare(strict_types=1); -namespace MongoDB\Builder\Expression; +namespace MongoDB\Builder\Accumulator; use MongoDB\BSON\Decimal128; use MongoDB\BSON\Int64; use MongoDB\BSON\UTCDateTime; +use MongoDB\Builder\Expression\ResolvesToDate; +use MongoDB\Builder\Expression\ResolvesToNumber; +use MongoDB\Builder\Expression\ResolvesToString; use MongoDB\Builder\Type\Encode; use MongoDB\Builder\Type\OperatorInterface; use MongoDB\Builder\Type\Optional; +use MongoDB\Builder\Type\WindowInterface; /** * Returns the approximation of the area under a curve. @@ -21,7 +25,7 @@ * * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/integral/ */ -class IntegralOperator implements ResolvesToDouble, ResolvesToDecimal, OperatorInterface +class IntegralAccumulator implements WindowInterface, OperatorInterface { public const ENCODE = Encode::Object; diff --git a/src/Builder/Expression/LinearFillOperator.php b/src/Builder/Accumulator/LinearFillAccumulator.php similarity index 83% rename from src/Builder/Expression/LinearFillOperator.php rename to src/Builder/Accumulator/LinearFillAccumulator.php index c01873e..e203f7f 100644 --- a/src/Builder/Expression/LinearFillOperator.php +++ b/src/Builder/Accumulator/LinearFillAccumulator.php @@ -6,12 +6,14 @@ declare(strict_types=1); -namespace MongoDB\Builder\Expression; +namespace MongoDB\Builder\Accumulator; use MongoDB\BSON\Decimal128; use MongoDB\BSON\Int64; +use MongoDB\Builder\Expression\ResolvesToNumber; use MongoDB\Builder\Type\Encode; use MongoDB\Builder\Type\OperatorInterface; +use MongoDB\Builder\Type\WindowInterface; /** * Fills null and missing fields in a window using linear interpolation based on surrounding field values. @@ -20,7 +22,7 @@ * * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/linearFill/ */ -class LinearFillOperator implements ResolvesToNumber, OperatorInterface +class LinearFillAccumulator implements WindowInterface, OperatorInterface { public const ENCODE = Encode::Single; diff --git a/src/Builder/Expression/LocfOperator.php b/src/Builder/Accumulator/LocfAccumulator.php similarity index 88% rename from src/Builder/Expression/LocfOperator.php rename to src/Builder/Accumulator/LocfAccumulator.php index 64d437a..e133cc4 100644 --- a/src/Builder/Expression/LocfOperator.php +++ b/src/Builder/Accumulator/LocfAccumulator.php @@ -6,12 +6,13 @@ declare(strict_types=1); -namespace MongoDB\Builder\Expression; +namespace MongoDB\Builder\Accumulator; use MongoDB\BSON\Type; use MongoDB\Builder\Type\Encode; use MongoDB\Builder\Type\ExpressionInterface; use MongoDB\Builder\Type\OperatorInterface; +use MongoDB\Builder\Type\WindowInterface; use stdClass; /** @@ -21,7 +22,7 @@ * * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/locf/ */ -class LocfOperator implements ResolvesToAny, OperatorInterface +class LocfAccumulator implements WindowInterface, OperatorInterface { public const ENCODE = Encode::Single; diff --git a/src/Builder/Expression/RankOperator.php b/src/Builder/Accumulator/RankAccumulator.php similarity index 79% rename from src/Builder/Expression/RankOperator.php rename to src/Builder/Accumulator/RankAccumulator.php index e7522ae..dd274dc 100644 --- a/src/Builder/Expression/RankOperator.php +++ b/src/Builder/Accumulator/RankAccumulator.php @@ -6,10 +6,11 @@ declare(strict_types=1); -namespace MongoDB\Builder\Expression; +namespace MongoDB\Builder\Accumulator; use MongoDB\Builder\Type\Encode; use MongoDB\Builder\Type\OperatorInterface; +use MongoDB\Builder\Type\WindowInterface; /** * Returns the document position (known as the rank) relative to other documents in the $setWindowFields stage partition. @@ -17,7 +18,7 @@ * * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/rank/ */ -class RankOperator implements ResolvesToInt, OperatorInterface +class RankAccumulator implements WindowInterface, OperatorInterface { public const ENCODE = Encode::Object; diff --git a/src/Builder/Expression/FactoryTrait.php b/src/Builder/Expression/FactoryTrait.php index 4611c08..0cc96a9 100644 --- a/src/Builder/Expression/FactoryTrait.php +++ b/src/Builder/Expression/FactoryTrait.php @@ -934,23 +934,6 @@ public static function indexOfCP( return new IndexOfCPOperator($string, $substring, $start, $end); } - /** - * Returns the approximation of the area under a curve. - * New in MongoDB 5.0. - * - * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/integral/ - * @param Decimal128|Int64|ResolvesToDate|ResolvesToNumber|UTCDateTime|float|int $input - * @param Optional|ResolvesToString|string $unit A string that specifies the time unit. Use one of these strings: "week", "day","hour", "minute", "second", "millisecond". - * If the sortBy field is not a date, you must omit a unit. If you specify a unit, you must specify a date in the sortBy field. - */ - public static function integral( - Decimal128|Int64|UTCDateTime|ResolvesToDate|ResolvesToNumber|float|int $input, - Optional|ResolvesToString|string $unit = Optional::Undefined, - ): IntegralOperator - { - return new IntegralOperator($input, $unit); - } - /** * Determines if the operand is an array. Returns a boolean. * @@ -1067,19 +1050,6 @@ public static function let( return new LetOperator($vars, $in); } - /** - * Fills null and missing fields in a window using linear interpolation based on surrounding field values. - * Available in the $setWindowFields stage. - * New in MongoDB 5.3. - * - * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/linearFill/ - * @param Decimal128|Int64|ResolvesToNumber|float|int $expression - */ - public static function linearFill(Decimal128|Int64|ResolvesToNumber|float|int $expression): LinearFillOperator - { - return new LinearFillOperator($expression); - } - /** * Return a value without parsing. Use for values that the aggregation pipeline may interpret as an expression. For example, use a $literal expression to a string that starts with a dollar sign ($) to avoid parsing as a field path. * @@ -1103,21 +1073,6 @@ public static function ln(Decimal128|Int64|ResolvesToNumber|float|int $number): return new LnOperator($number); } - /** - * Last observation carried forward. Sets values for null and missing fields in a window to the last non-null value for the field. - * Available in the $setWindowFields stage. - * New in MongoDB 5.2. - * - * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/locf/ - * @param ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression - */ - public static function locf( - Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression, - ): LocfOperator - { - return new LocfOperator($expression); - } - /** * Calculates the log of a number in the specified base. * @@ -1523,17 +1478,6 @@ public static function range( return new RangeOperator($start, $end, $step); } - /** - * Returns the document position (known as the rank) relative to other documents in the $setWindowFields stage partition. - * New in MongoDB 5.0. - * - * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/rank/ - */ - public static function rank(): RankOperator - { - return new RankOperator(); - } - /** * Applies an expression to each element in an array and combines them into a single value. * diff --git a/tests/Builder/Accumulator/CovariancePopAccumulatorTest.php b/tests/Builder/Accumulator/CovariancePopAccumulatorTest.php new file mode 100644 index 0000000..a07eb3e --- /dev/null +++ b/tests/Builder/Accumulator/CovariancePopAccumulatorTest.php @@ -0,0 +1,44 @@ +assertSamePipeline(Pipelines::CovariancePopExample, $pipeline); + } +} diff --git a/tests/Builder/Accumulator/CovarianceSampAccumulatorTest.php b/tests/Builder/Accumulator/CovarianceSampAccumulatorTest.php new file mode 100644 index 0000000..a0db117 --- /dev/null +++ b/tests/Builder/Accumulator/CovarianceSampAccumulatorTest.php @@ -0,0 +1,44 @@ +assertSamePipeline(Pipelines::CovarianceSampExample, $pipeline); + } +} diff --git a/tests/Builder/Accumulator/DenseRankAccumulatorTest.php b/tests/Builder/Accumulator/DenseRankAccumulatorTest.php new file mode 100644 index 0000000..54dac4b --- /dev/null +++ b/tests/Builder/Accumulator/DenseRankAccumulatorTest.php @@ -0,0 +1,56 @@ +assertSamePipeline(Pipelines::DenseRankDenseRankPartitionsByADateField, $pipeline); + } + + public function testDenseRankPartitionsByAnIntegerField(): void + { + $pipeline = new Pipeline( + Stage::setWindowFields( + partitionBy: Expression::stringFieldPath('state'), + sortBy: object( + quantity: -1, + ), + output: object( + // The outputWindow is optional when no window property is set. + denseRankQuantityForState: Accumulator::denseRank(), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::DenseRankDenseRankPartitionsByAnIntegerField, $pipeline); + } +} diff --git a/tests/Builder/Accumulator/DerivativeAccumulatorTest.php b/tests/Builder/Accumulator/DerivativeAccumulatorTest.php new file mode 100644 index 0000000..448d113 --- /dev/null +++ b/tests/Builder/Accumulator/DerivativeAccumulatorTest.php @@ -0,0 +1,47 @@ +assertSamePipeline(Pipelines::DerivativeExample, $pipeline); + } +} diff --git a/tests/Builder/Accumulator/DocumentNumberAccumulatorTest.php b/tests/Builder/Accumulator/DocumentNumberAccumulatorTest.php new file mode 100644 index 0000000..832fd4c --- /dev/null +++ b/tests/Builder/Accumulator/DocumentNumberAccumulatorTest.php @@ -0,0 +1,36 @@ +assertSamePipeline(Pipelines::DocumentNumberDocumentNumberForEachState, $pipeline); + } +} diff --git a/tests/Builder/Accumulator/ExpMovingAvgAccumulatorTest.php b/tests/Builder/Accumulator/ExpMovingAvgAccumulatorTest.php new file mode 100644 index 0000000..2e1a33b --- /dev/null +++ b/tests/Builder/Accumulator/ExpMovingAvgAccumulatorTest.php @@ -0,0 +1,59 @@ +assertSamePipeline(Pipelines::ExpMovingAvgExponentialMovingAverageUsingAlpha, $pipeline); + } + + public function testExponentialMovingAverageUsingN(): void + { + $pipeline = new Pipeline( + Stage::setWindowFields( + partitionBy: Expression::stringFieldPath('stock'), + sortBy: object( + date: 1, + ), + output: object( + expMovingAvgForStock: Accumulator::expMovingAvg( + input: Expression::numberFieldPath('price'), + N: 2, + ), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::ExpMovingAvgExponentialMovingAverageUsingN, $pipeline); + } +} diff --git a/tests/Builder/Accumulator/IntegralAccumulatorTest.php b/tests/Builder/Accumulator/IntegralAccumulatorTest.php new file mode 100644 index 0000000..d596d12 --- /dev/null +++ b/tests/Builder/Accumulator/IntegralAccumulatorTest.php @@ -0,0 +1,43 @@ +assertSamePipeline(Pipelines::IntegralExample, $pipeline); + } +} diff --git a/tests/Builder/Accumulator/LinearFillAccumulatorTest.php b/tests/Builder/Accumulator/LinearFillAccumulatorTest.php new file mode 100644 index 0000000..32c4a02 --- /dev/null +++ b/tests/Builder/Accumulator/LinearFillAccumulatorTest.php @@ -0,0 +1,58 @@ +assertSamePipeline(Pipelines::LinearFillFillMissingValuesWithLinearInterpolation, $pipeline); + } + + public function testUseMultipleFillMethodsInASingleStage(): void + { + $pipeline = new Pipeline( + Stage::setWindowFields( + sortBy: object( + time: 1, + ), + output: object( + linearFillPrice: Accumulator::linearFill( + Expression::numberFieldPath('price'), + ), + locfPrice: Accumulator::locf( + Expression::numberFieldPath('price'), + ), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::LinearFillUseMultipleFillMethodsInASingleStage, $pipeline); + } +} diff --git a/tests/Builder/Accumulator/LocfAccumulatorTest.php b/tests/Builder/Accumulator/LocfAccumulatorTest.php new file mode 100644 index 0000000..0723e9a --- /dev/null +++ b/tests/Builder/Accumulator/LocfAccumulatorTest.php @@ -0,0 +1,37 @@ +assertSamePipeline(Pipelines::LocfFillMissingValuesWithTheLastObservedValue, $pipeline); + } +} diff --git a/tests/Builder/Accumulator/MinNAccumulatorTest.php b/tests/Builder/Accumulator/MinNAccumulatorTest.php new file mode 100644 index 0000000..c44c1f6 --- /dev/null +++ b/tests/Builder/Accumulator/MinNAccumulatorTest.php @@ -0,0 +1,85 @@ +assertSamePipeline(Pipelines::MinNComputingNBasedOnTheGroupKeyForGroup, $pipeline); + } + + public function testFindTheMinimumThreeScoresForASingleGame(): void + { + $pipeline = new Pipeline( + Stage::match( + gameId: 'G1', + ), + Stage::group( + _id: Expression::fieldPath('gameId'), + minScores: Accumulator::minN( + input: [ + Expression::fieldPath('score'), + Expression::fieldPath('playerId'), + ], + n: 3, + ), + ), + ); + + $this->assertSamePipeline(Pipelines::MinNFindTheMinimumThreeScoresForASingleGame, $pipeline); + } + + public function testFindingTheMinimumThreeDocumentsAcrossMultipleGames(): void + { + $pipeline = new Pipeline( + Stage::group( + _id: Expression::fieldPath('gameId'), + minScores: Accumulator::minN( + input: [ + Expression::fieldPath('score'), + Expression::fieldPath('playerId'), + ], + n: 3, + ), + ), + ); + + $this->assertSamePipeline(Pipelines::MinNFindingTheMinimumThreeDocumentsAcrossMultipleGames, $pipeline); + } +} diff --git a/tests/Builder/Accumulator/Pipelines.php b/tests/Builder/Accumulator/Pipelines.php index d0baa96..fedc91a 100644 --- a/tests/Builder/Accumulator/Pipelines.php +++ b/tests/Builder/Accumulator/Pipelines.php @@ -423,6 +423,265 @@ enum Pipelines: string ] JSON; + /** + * Example + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/covariancePop/#example + */ + case CovariancePopExample = <<<'JSON' + [ + { + "$setWindowFields": { + "partitionBy": "$state", + "sortBy": { + "orderDate": { + "$numberInt": "1" + } + }, + "output": { + "covariancePopForState": { + "$covariancePop": [ + { + "$year": { + "date": "$orderDate" + } + }, + "$quantity" + ], + "window": { + "documents": [ + "unbounded", + "current" + ] + } + } + } + } + } + ] + JSON; + + /** + * Example + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/covarianceSamp/#example + */ + case CovarianceSampExample = <<<'JSON' + [ + { + "$setWindowFields": { + "partitionBy": "$state", + "sortBy": { + "orderDate": { + "$numberInt": "1" + } + }, + "output": { + "covarianceSampForState": { + "$covarianceSamp": [ + { + "$year": { + "date": "$orderDate" + } + }, + "$quantity" + ], + "window": { + "documents": [ + "unbounded", + "current" + ] + } + } + } + } + } + ] + JSON; + + /** + * Dense Rank Partitions by an Integer Field + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/denseRank/#dense-rank-partitions-by-an-integer-field + */ + case DenseRankDenseRankPartitionsByAnIntegerField = <<<'JSON' + [ + { + "$setWindowFields": { + "partitionBy": "$state", + "sortBy": { + "quantity": { + "$numberInt": "-1" + } + }, + "output": { + "denseRankQuantityForState": { + "$denseRank": {} + } + } + } + } + ] + JSON; + + /** + * Dense Rank Partitions by a Date Field + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/denseRank/#dense-rank-partitions-by-a-date-field + */ + case DenseRankDenseRankPartitionsByADateField = <<<'JSON' + [ + { + "$setWindowFields": { + "partitionBy": "$state", + "sortBy": { + "orderDate": { + "$numberInt": "1" + } + }, + "output": { + "denseRankOrderDateForState": { + "$denseRank": {} + } + } + } + } + ] + JSON; + + /** + * Example + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/derivative/#example + */ + case DerivativeExample = <<<'JSON' + [ + { + "$setWindowFields": { + "partitionBy": "$truckID", + "sortBy": { + "timeStamp": { + "$numberInt": "1" + } + }, + "output": { + "truckAverageSpeed": { + "$derivative": { + "input": "$miles", + "unit": "hour" + }, + "window": { + "range": [ + { + "$numberInt": "-30" + }, + { + "$numberInt": "0" + } + ], + "unit": "second" + } + } + } + } + }, + { + "$match": { + "truckAverageSpeed": { + "$gt": { + "$numberInt": "50" + } + } + } + } + ] + JSON; + + /** + * Document Number for Each State + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/documentNumber/#document-number-for-each-state + */ + case DocumentNumberDocumentNumberForEachState = <<<'JSON' + [ + { + "$setWindowFields": { + "partitionBy": "$state", + "sortBy": { + "quantity": { + "$numberInt": "-1" + } + }, + "output": { + "documentNumberForState": { + "$documentNumber": {} + } + } + } + } + ] + JSON; + + /** + * Exponential Moving Average Using N + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/expMovingAvg/#exponential-moving-average-using-n + */ + case ExpMovingAvgExponentialMovingAverageUsingN = <<<'JSON' + [ + { + "$setWindowFields": { + "partitionBy": "$stock", + "sortBy": { + "date": { + "$numberInt": "1" + } + }, + "output": { + "expMovingAvgForStock": { + "$expMovingAvg": { + "input": "$price", + "N": { + "$numberInt": "2" + } + } + } + } + } + } + ] + JSON; + + /** + * Exponential Moving Average Using alpha + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/expMovingAvg/#exponential-moving-average-using-alpha + */ + case ExpMovingAvgExponentialMovingAverageUsingAlpha = <<<'JSON' + [ + { + "$setWindowFields": { + "partitionBy": "$stock", + "sortBy": { + "date": { + "$numberInt": "1" + } + }, + "output": { + "expMovingAvgForStock": { + "$expMovingAvg": { + "input": "$price", + "alpha": { + "$numberDouble": "0.75" + } + } + } + } + } + } + ] + JSON; + /** * Use in $group Stage * @@ -667,6 +926,41 @@ enum Pipelines: string ] JSON; + /** + * Example + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/integral/#example + */ + case IntegralExample = <<<'JSON' + [ + { + "$setWindowFields": { + "partitionBy": "$powerMeterID", + "sortBy": { + "timeStamp": { + "$numberInt": "1" + } + }, + "output": { + "powerMeterKilowattHours": { + "$integral": { + "input": "$kilowatts", + "unit": "hour" + }, + "window": { + "range": [ + "unbounded", + "current" + ], + "unit": "hour" + } + } + } + } + } + ] + JSON; + /** * Use in $group Stage * @@ -854,6 +1148,81 @@ enum Pipelines: string ] JSON; + /** + * Fill Missing Values with Linear Interpolation + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/linearFill/#fill-missing-values-with-linear-interpolation + */ + case LinearFillFillMissingValuesWithLinearInterpolation = <<<'JSON' + [ + { + "$setWindowFields": { + "sortBy": { + "time": { + "$numberInt": "1" + } + }, + "output": { + "price": { + "$linearFill": "$price" + } + } + } + } + ] + JSON; + + /** + * Use Multiple Fill Methods in a Single Stage + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/linearFill/#use-multiple-fill-methods-in-a-single-stage + */ + case LinearFillUseMultipleFillMethodsInASingleStage = <<<'JSON' + [ + { + "$setWindowFields": { + "sortBy": { + "time": { + "$numberInt": "1" + } + }, + "output": { + "linearFillPrice": { + "$linearFill": "$price" + }, + "locfPrice": { + "$locf": "$price" + } + } + } + } + ] + JSON; + + /** + * Fill Missing Values with the Last Observed Value + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/locf/#fill-missing-values-with-the-last-observed-value + */ + case LocfFillMissingValuesWithTheLastObservedValue = <<<'JSON' + [ + { + "$setWindowFields": { + "sortBy": { + "time": { + "$numberInt": "1" + } + }, + "output": { + "price": { + "$locf": "$price" + } + } + } + } + ] + JSON; + /** * Use in $group Stage * @@ -1147,6 +1516,104 @@ enum Pipelines: string ] JSON; + /** + * Find the Minimum Three Scores for a Single Game + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/minN/#find-the-minimum-three-scores-for-a-single-game + */ + case MinNFindTheMinimumThreeScoresForASingleGame = <<<'JSON' + [ + { + "$match": { + "gameId": "G1" + } + }, + { + "$group": { + "_id": "$gameId", + "minScores": { + "$minN": { + "input": [ + "$score", + "$playerId" + ], + "n": { + "$numberInt": "3" + } + } + } + } + } + ] + JSON; + + /** + * Finding the Minimum Three Documents Across Multiple Games + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/minN/#finding-the-minimum-three-documents-across-multiple-games + */ + case MinNFindingTheMinimumThreeDocumentsAcrossMultipleGames = <<<'JSON' + [ + { + "$group": { + "_id": "$gameId", + "minScores": { + "$minN": { + "input": [ + "$score", + "$playerId" + ], + "n": { + "$numberInt": "3" + } + } + } + } + } + ] + JSON; + + /** + * Computing n Based on the Group Key for $group + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/minN/#computing-n-based-on-the-group-key-for--group + */ + case MinNComputingNBasedOnTheGroupKeyForGroup = <<<'JSON' + [ + { + "$group": { + "_id": { + "gameId": "$gameId" + }, + "gamescores": { + "$minN": { + "input": [ + "$score", + "$playerId" + ], + "n": { + "$cond": { + "if": { + "$eq": [ + "$gameId", + "G2" + ] + }, + "then": { + "$numberInt": "1" + }, + "else": { + "$numberInt": "3" + } + } + } + } + } + } + } + ] + JSON; + /** * Calculate a Single Value as an Accumulator * @@ -1396,6 +1863,118 @@ enum Pipelines: string ] JSON; + /** + * Rank Partitions by an Integer Field + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/rank/#rank-partitions-by-an-integer-field + */ + case RankRankPartitionsByAnIntegerField = <<<'JSON' + [ + { + "$setWindowFields": { + "partitionBy": "$state", + "sortBy": { + "quantity": { + "$numberInt": "-1" + } + }, + "output": { + "rankQuantityForState": { + "$rank": {} + } + } + } + } + ] + JSON; + + /** + * Rank Partitions by a Date Field + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/rank/#rank-partitions-by-a-date-field + */ + case RankRankPartitionsByADateField = <<<'JSON' + [ + { + "$setWindowFields": { + "partitionBy": "$state", + "sortBy": { + "orderDate": { + "$numberInt": "1" + } + }, + "output": { + "rankOrderDateForState": { + "$rank": {} + } + } + } + } + ] + JSON; + + /** + * Shift Using a Positive Integer + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/shift/#shift-using-a-positive-integer + */ + case ShiftShiftUsingAPositiveInteger = <<<'JSON' + [ + { + "$setWindowFields": { + "partitionBy": "$state", + "sortBy": { + "quantity": { + "$numberInt": "-1" + } + }, + "output": { + "shiftQuantityForState": { + "$shift": { + "output": "$quantity", + "by": { + "$numberInt": "1" + }, + "default": "Not available" + } + } + } + } + } + ] + JSON; + + /** + * Shift Using a Negative Integer + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/shift/#shift-using-a-negative-integer + */ + case ShiftShiftUsingANegativeInteger = <<<'JSON' + [ + { + "$setWindowFields": { + "partitionBy": "$state", + "sortBy": { + "quantity": { + "$numberInt": "-1" + } + }, + "output": { + "shiftQuantityForState": { + "$shift": { + "output": "$quantity", + "by": { + "$numberInt": "-1" + }, + "default": "Not available" + } + } + } + } + } + ] + JSON; + /** * Use in $group Stage * diff --git a/tests/Builder/Accumulator/RankAccumulatorTest.php b/tests/Builder/Accumulator/RankAccumulatorTest.php new file mode 100644 index 0000000..d2420a9 --- /dev/null +++ b/tests/Builder/Accumulator/RankAccumulatorTest.php @@ -0,0 +1,53 @@ +assertSamePipeline(Pipelines::RankRankPartitionsByADateField, $pipeline); + } + + public function testRankPartitionsByAnIntegerField(): void + { + $pipeline = new Pipeline( + Stage::setWindowFields( + partitionBy: Expression::stringFieldPath('state'), + sortBy: object( + quantity: -1, + ), + output: object( + rankQuantityForState: Accumulator::rank(), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::RankRankPartitionsByAnIntegerField, $pipeline); + } +} diff --git a/tests/Builder/Accumulator/ShiftAccumulatorTest.php b/tests/Builder/Accumulator/ShiftAccumulatorTest.php new file mode 100644 index 0000000..c36c680 --- /dev/null +++ b/tests/Builder/Accumulator/ShiftAccumulatorTest.php @@ -0,0 +1,61 @@ +assertSamePipeline(Pipelines::ShiftShiftUsingANegativeInteger, $pipeline); + } + + public function testShiftUsingAPositiveInteger(): void + { + $pipeline = new Pipeline( + Stage::setWindowFields( + partitionBy: Expression::stringFieldPath('state'), + sortBy: object( + quantity: -1, + ), + output: object( + shiftQuantityForState: Accumulator::shift( + output: Expression::fieldPath('quantity'), + by: 1, + default: 'Not available', + ), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::ShiftShiftUsingAPositiveInteger, $pipeline); + } +}