Skip to content

Accumulators API

This page documents all accumulator classes used with the $group stage.

Basic Accumulators

Sum

Bases: Accumulator

$sum accumulator - sums numeric values.

Example

Sum(name="totalQuantity", field="quantity").model_dump() {"totalQuantity": {"$sum": "$quantity"}}

Sum(name="count", value=1).model_dump() {"count": {"$sum": 1}}

Source code in mongo_aggro/accumulators.py
class Sum(Accumulator):
    """
    $sum accumulator - sums numeric values.

    Example:
        >>> Sum(name="totalQuantity", field="quantity").model_dump()
        {"totalQuantity": {"$sum": "$quantity"}}

        >>> Sum(name="count", value=1).model_dump()
        {"count": {"$sum": 1}}
    """

    field: str | None = Field(
        default=None, description="Field path to sum (without $)"
    )
    value: int | float | None = Field(
        default=None, description="Literal value to sum (e.g., 1 for counting)"
    )

    def model_dump(self, **kwargs: Any) -> dict[str, dict[str, Any]]:
        if self.field is not None:
            field_path = (
                f"${self.field}"
                if not self.field.startswith("$")
                else self.field
            )
            return {self.name: {"$sum": field_path}}
        return {self.name: {"$sum": self.value}}

Avg

Bases: Accumulator

$avg accumulator - calculates average of numeric values.

Example

Avg(name="avgPrice", field="price").model_dump() {"avgPrice": {"$avg": "$price"}}

Source code in mongo_aggro/accumulators.py
class Avg(Accumulator):
    """
    $avg accumulator - calculates average of numeric values.

    Example:
        >>> Avg(name="avgPrice", field="price").model_dump()
        {"avgPrice": {"$avg": "$price"}}
    """

    field: str = Field(..., description="Field path to average (without $)")

    def model_dump(self, **kwargs: Any) -> dict[str, dict[str, Any]]:
        field_path = (
            f"${self.field}" if not self.field.startswith("$") else self.field
        )
        return {self.name: {"$avg": field_path}}

Min

Bases: Accumulator

$min accumulator - returns minimum value.

Example

Min(name="minPrice", field="price").model_dump() {"minPrice": {"$min": "$price"}}

Source code in mongo_aggro/accumulators.py
class Min(Accumulator):
    """
    $min accumulator - returns minimum value.

    Example:
        >>> Min(name="minPrice", field="price").model_dump()
        {"minPrice": {"$min": "$price"}}
    """

    field: str = Field(..., description="Field path (without $)")

    def model_dump(self, **kwargs: Any) -> dict[str, dict[str, Any]]:
        field_path = (
            f"${self.field}" if not self.field.startswith("$") else self.field
        )
        return {self.name: {"$min": field_path}}

Max

Bases: Accumulator

$max accumulator - returns maximum value.

Example

Max(name="maxPrice", field="price").model_dump() {"maxPrice": {"$max": "$price"}}

Source code in mongo_aggro/accumulators.py
class Max(Accumulator):
    """
    $max accumulator - returns maximum value.

    Example:
        >>> Max(name="maxPrice", field="price").model_dump()
        {"maxPrice": {"$max": "$price"}}
    """

    field: str = Field(..., description="Field path (without $)")

    def model_dump(self, **kwargs: Any) -> dict[str, dict[str, Any]]:
        field_path = (
            f"${self.field}" if not self.field.startswith("$") else self.field
        )
        return {self.name: {"$max": field_path}}

First

Bases: Accumulator

$first accumulator - returns first value in group.

Example

First(name="firstItem", field="item").model_dump() {"firstItem": {"$first": "$item"}}

Source code in mongo_aggro/accumulators.py
class First(Accumulator):
    """
    $first accumulator - returns first value in group.

    Example:
        >>> First(name="firstItem", field="item").model_dump()
        {"firstItem": {"$first": "$item"}}
    """

    field: str = Field(..., description="Field path (without $)")

    def model_dump(self, **kwargs: Any) -> dict[str, dict[str, Any]]:
        field_path = (
            f"${self.field}" if not self.field.startswith("$") else self.field
        )
        return {self.name: {"$first": field_path}}

Last

Bases: Accumulator

$last accumulator - returns last value in group.

Example

Last(name="lastItem", field="item").model_dump() {"lastItem": {"$last": "$item"}}

Source code in mongo_aggro/accumulators.py
class Last(Accumulator):
    """
    $last accumulator - returns last value in group.

    Example:
        >>> Last(name="lastItem", field="item").model_dump()
        {"lastItem": {"$last": "$item"}}
    """

    field: str = Field(..., description="Field path (without $)")

    def model_dump(self, **kwargs: Any) -> dict[str, dict[str, Any]]:
        field_path = (
            f"${self.field}" if not self.field.startswith("$") else self.field
        )
        return {self.name: {"$last": field_path}}

Array Accumulators

Push

Bases: Accumulator

$push accumulator - creates array of values.

Example

Push(name="items", field="item").model_dump() {"items": {"$push": "$item"}}

Push( name="details", expression={"name": "$name", "qty": "$qty"} ).model_dump() {"details": {"$push": {"name": "$name", "qty": "$qty"}}}

Source code in mongo_aggro/accumulators.py
class Push(Accumulator):
    """
    $push accumulator - creates array of values.

    Example:
        >>> Push(name="items", field="item").model_dump()
        {"items": {"$push": "$item"}}

        >>> Push(
            name="details",
            expression={"name": "$name", "qty": "$qty"}
        ).model_dump()
        {"details": {"$push": {"name": "$name", "qty": "$qty"}}}
    """

    field: str | None = Field(
        default=None, description="Field path to push (without $)"
    )
    expression: dict[str, Any] | None = Field(
        default=None, description="Expression to push"
    )

    def model_dump(self, **kwargs: Any) -> dict[str, dict[str, Any]]:
        if self.expression is not None:
            return {self.name: {"$push": self.expression}}
        field_path = (
            f"${self.field}"
            if self.field and not self.field.startswith("$")
            else self.field
        )
        return {self.name: {"$push": field_path}}

AddToSet

Bases: Accumulator

$addToSet accumulator - creates array of unique values.

Example

AddToSet(name="uniqueTags", field="tag").model_dump() {"uniqueTags": {"$addToSet": "$tag"}}

Source code in mongo_aggro/accumulators.py
class AddToSet(Accumulator):
    """
    $addToSet accumulator - creates array of unique values.

    Example:
        >>> AddToSet(name="uniqueTags", field="tag").model_dump()
        {"uniqueTags": {"$addToSet": "$tag"}}
    """

    field: str = Field(..., description="Field path (without $)")

    def model_dump(self, **kwargs: Any) -> dict[str, dict[str, Any]]:
        field_path = (
            f"${self.field}" if not self.field.startswith("$") else self.field
        )
        return {self.name: {"$addToSet": field_path}}

Statistical Accumulators

StdDevPop

Bases: Accumulator

$stdDevPop accumulator - population standard deviation.

Example

StdDevPop(name="stdDev", field="score").model_dump() {"stdDev": {"$stdDevPop": "$score"}}

Source code in mongo_aggro/accumulators.py
class StdDevPop(Accumulator):
    """
    $stdDevPop accumulator - population standard deviation.

    Example:
        >>> StdDevPop(name="stdDev", field="score").model_dump()
        {"stdDev": {"$stdDevPop": "$score"}}
    """

    field: str = Field(..., description="Field path (without $)")

    def model_dump(self, **kwargs: Any) -> dict[str, dict[str, Any]]:
        field_path = (
            f"${self.field}" if not self.field.startswith("$") else self.field
        )
        return {self.name: {"$stdDevPop": field_path}}

StdDevSamp

Bases: Accumulator

$stdDevSamp accumulator - sample standard deviation.

Example

StdDevSamp(name="stdDevSample", field="score").model_dump() {"stdDevSample": {"$stdDevSamp": "$score"}}

Source code in mongo_aggro/accumulators.py
class StdDevSamp(Accumulator):
    """
    $stdDevSamp accumulator - sample standard deviation.

    Example:
        >>> StdDevSamp(name="stdDevSample", field="score").model_dump()
        {"stdDevSample": {"$stdDevSamp": "$score"}}
    """

    field: str = Field(..., description="Field path (without $)")

    def model_dump(self, **kwargs: Any) -> dict[str, dict[str, Any]]:
        field_path = (
            f"${self.field}" if not self.field.startswith("$") else self.field
        )
        return {self.name: {"$stdDevSamp": field_path}}

Count Accumulator

Count_

Bases: Accumulator

$count accumulator - counts documents in group (MongoDB 5.0+).

Example

Count_(name="totalDocs").model_dump() {"totalDocs": {"$count": {}}}

Source code in mongo_aggro/accumulators.py
class Count_(Accumulator):
    """
    $count accumulator - counts documents in group (MongoDB 5.0+).

    Example:
        >>> Count_(name="totalDocs").model_dump()
        {"totalDocs": {"$count": {}}}
    """

    def model_dump(self, **kwargs: Any) -> dict[str, dict[str, Any]]:
        return {self.name: {"$count": {}}}

Object Accumulators

MergeObjects

Bases: Accumulator

$mergeObjects accumulator - merges documents into single document.

Example

MergeObjects(name="merged", field="details").model_dump() {"merged": {"$mergeObjects": "$details"}}

Source code in mongo_aggro/accumulators.py
class MergeObjects(Accumulator):
    """
    $mergeObjects accumulator - merges documents into single document.

    Example:
        >>> MergeObjects(name="merged", field="details").model_dump()
        {"merged": {"$mergeObjects": "$details"}}
    """

    field: str = Field(..., description="Field path (without $)")

    def model_dump(self, **kwargs: Any) -> dict[str, dict[str, Any]]:
        field_path = (
            f"${self.field}" if not self.field.startswith("$") else self.field
        )
        return {self.name: {"$mergeObjects": field_path}}

N-Value Accumulators

TopN

Bases: Accumulator

$topN accumulator - returns top N elements (MongoDB 5.2+).

Example

TopN( ... name="top3", ... n=3, ... sort_by={"score": -1}, ... output="$item" ... ).model_dump() { "top3": { "$topN": {"n": 3, "sortBy": {"score": -1}, "output": "$item"} } }

Source code in mongo_aggro/accumulators.py
class TopN(Accumulator):
    """
    $topN accumulator - returns top N elements (MongoDB 5.2+).

    Example:
        >>> TopN(
        ...     name="top3",
        ...     n=3,
        ...     sort_by={"score": -1},
        ...     output="$item"
        ... ).model_dump()
        {
            "top3": {
                "$topN": {"n": 3, "sortBy": {"score": -1},
                "output": "$item"}
            }
        }
    """

    n: int = Field(..., gt=0, description="Number of results")
    sort_by: dict[str, int] = Field(
        ...,
        validation_alias="sortBy",
        serialization_alias="sortBy",
        description="Sort specification",
    )
    output: str | dict[str, Any] = Field(..., description="Output expression")

    def model_dump(self, **kwargs: Any) -> dict[str, dict[str, Any]]:
        return {
            self.name: {
                "$topN": {
                    "n": self.n,
                    "sortBy": self.sort_by,
                    "output": self.output,
                }
            }
        }

BottomN

Bases: Accumulator

$bottomN accumulator - returns bottom N elements (MongoDB 5.2+).

Example

BottomN( ... name="bottom3", ... n=3, ... sort_by={"score": -1}, ... output="$item" ... ).model_dump() { "bottom3": { "$bottomN": {"n": 3, "sortBy": {"score": -1}, "output": "$item"} } }

Source code in mongo_aggro/accumulators.py
class BottomN(Accumulator):
    """
    $bottomN accumulator - returns bottom N elements (MongoDB 5.2+).

    Example:
        >>> BottomN(
        ...     name="bottom3",
        ...     n=3,
        ...     sort_by={"score": -1},
        ...     output="$item"
        ... ).model_dump()
        {
            "bottom3": {
                "$bottomN": {"n": 3, "sortBy": {"score": -1},
                "output": "$item"}
            }
        }
    """

    n: int = Field(..., gt=0, description="Number of results")
    sort_by: dict[str, int] = Field(
        ...,
        validation_alias="sortBy",
        serialization_alias="sortBy",
        description="Sort specification",
    )
    output: str | dict[str, Any] = Field(..., description="Output expression")

    def model_dump(self, **kwargs: Any) -> dict[str, dict[str, Any]]:
        return {
            self.name: {
                "$bottomN": {
                    "n": self.n,
                    "sortBy": self.sort_by,
                    "output": self.output,
                }
            }
        }

FirstN

Bases: Accumulator

$firstN accumulator - returns first N elements (MongoDB 5.2+).

Example

FirstN(name="first3", n=3, input="$item").model_dump() {"first3": {"$firstN": {"n": 3, "input": "$item"}}}

Source code in mongo_aggro/accumulators.py
class FirstN(Accumulator):
    """
    $firstN accumulator - returns first N elements (MongoDB 5.2+).

    Example:
        >>> FirstN(name="first3", n=3, input="$item").model_dump()
        {"first3": {"$firstN": {"n": 3, "input": "$item"}}}
    """

    n: int = Field(..., gt=0, description="Number of results")
    input: str | dict[str, Any] = Field(..., description="Input expression")

    def model_dump(self, **kwargs: Any) -> dict[str, dict[str, Any]]:
        return {self.name: {"$firstN": {"n": self.n, "input": self.input}}}

LastN

Bases: Accumulator

$lastN accumulator - returns last N elements (MongoDB 5.2+).

Example

LastN(name="last3", n=3, input="$item").model_dump() {"last3": {"$lastN": {"n": 3, "input": "$item"}}}

Source code in mongo_aggro/accumulators.py
class LastN(Accumulator):
    """
    $lastN accumulator - returns last N elements (MongoDB 5.2+).

    Example:
        >>> LastN(name="last3", n=3, input="$item").model_dump()
        {"last3": {"$lastN": {"n": 3, "input": "$item"}}}
    """

    n: int = Field(..., gt=0, description="Number of results")
    input: str | dict[str, Any] = Field(..., description="Input expression")

    def model_dump(self, **kwargs: Any) -> dict[str, dict[str, Any]]:
        return {self.name: {"$lastN": {"n": self.n, "input": self.input}}}

MaxN

Bases: Accumulator

$maxN accumulator - returns N maximum values (MongoDB 5.2+).

Example

MaxN(name="top3Scores", n=3, input="$score").model_dump() {"top3Scores": {"$maxN": {"n": 3, "input": "$score"}}}

Source code in mongo_aggro/accumulators.py
class MaxN(Accumulator):
    """
    $maxN accumulator - returns N maximum values (MongoDB 5.2+).

    Example:
        >>> MaxN(name="top3Scores", n=3, input="$score").model_dump()
        {"top3Scores": {"$maxN": {"n": 3, "input": "$score"}}}
    """

    n: int = Field(..., gt=0, description="Number of results")
    input: str | dict[str, Any] = Field(..., description="Input expression")

    def model_dump(self, **kwargs: Any) -> dict[str, dict[str, Any]]:
        return {self.name: {"$maxN": {"n": self.n, "input": self.input}}}

MinN

Bases: Accumulator

$minN accumulator - returns N minimum values (MongoDB 5.2+).

Example

MinN(name="lowest3", n=3, input="$score").model_dump() {"lowest3": {"$minN": {"n": 3, "input": "$score"}}}

Source code in mongo_aggro/accumulators.py
class MinN(Accumulator):
    """
    $minN accumulator - returns N minimum values (MongoDB 5.2+).

    Example:
        >>> MinN(name="lowest3", n=3, input="$score").model_dump()
        {"lowest3": {"$minN": {"n": 3, "input": "$score"}}}
    """

    n: int = Field(..., gt=0, description="Number of results")
    input: str | dict[str, Any] = Field(..., description="Input expression")

    def model_dump(self, **kwargs: Any) -> dict[str, dict[str, Any]]:
        return {self.name: {"$minN": {"n": self.n, "input": self.input}}}

Helper Functions

merge_accumulators

merge_accumulators(*accumulators: Accumulator) -> dict[str, Any]

Combine typed accumulator instances into a dict for Group stage.

This helper provides type-safe accumulator definitions with IDE autocomplete and validation, instead of writing raw MongoDB dicts.

Parameters:

Name Type Description Default
*accumulators Accumulator

Accumulator instances (Sum, Avg, Max, etc.)

()

Returns:

Type Description
dict[str, Any]

Combined dict suitable for Group's accumulators parameter.

Example

Using typed accumulators (recommended for type safety):

Group( ... id="$category", ... accumulators=merge_accumulators( ... Sum(name="total", field="amount"), ... Avg(name="average", field="price"), ... Count_(name="count"), ... ) ... )

Equivalent raw dict (less type safety):

Group( ... id="$category", ... accumulators={ ... "total": {"$sum": "$amount"}, ... "average": {"$avg": "$price"}, ... "count": {"$count": {}}, ... } ... )

Source code in mongo_aggro/accumulators.py
def merge_accumulators(*accumulators: Accumulator) -> dict[str, Any]:
    """
    Combine typed accumulator instances into a dict for Group stage.

    This helper provides type-safe accumulator definitions with IDE
    autocomplete and validation, instead of writing raw MongoDB dicts.

    Args:
        *accumulators: Accumulator instances (Sum, Avg, Max, etc.)

    Returns:
        Combined dict suitable for Group's accumulators parameter.

    Example:
        Using typed accumulators (recommended for type safety):

        >>> Group(
        ...     id="$category",
        ...     accumulators=merge_accumulators(
        ...         Sum(name="total", field="amount"),
        ...         Avg(name="average", field="price"),
        ...         Count_(name="count"),
        ...     )
        ... )

        Equivalent raw dict (less type safety):

        >>> Group(
        ...     id="$category",
        ...     accumulators={
        ...         "total": {"$sum": "$amount"},
        ...         "average": {"$avg": "$price"},
        ...         "count": {"$count": {}},
        ...     }
        ... )
    """
    result: dict[str, Any] = {}
    for acc in accumulators:
        result.update(acc.model_dump())
    return result