> For the complete documentation index, see [llms.txt](https://codifi-fdm.gitbook.io/codifi-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://codifi-fdm.gitbook.io/codifi-docs/cross-platform-features/ripple/aggregation-formulas.md).

# Aggregation Formulas

Ripple can aggregate data across **repeater rows** within a single Record and across **child Records** in the Project hierarchy. This enables powerful roll-up summaries at every level of your Project.

***

## Repeater Row Aggregation

Repeater sections let users add multiple rows of data — for example, multiple artifacts in one bag, or multiple layers in a soil profile. Ripple can aggregate across these rows.

| Function             | Description                            | Example                                |
| -------------------- | -------------------------------------- | -------------------------------------- |
| `sumRepeater`        | Add up a numeric field across all rows | Total weight of all artifacts in a bag |
| `avgRepeater`        | Average of a field across rows         | Average artifact size                  |
| `minRepeater`        | Smallest value in a field across rows  | Shallowest depth                       |
| `maxRepeater`        | Largest value in a field across rows   | Deepest depth                          |
| `repeaterCount`      | Number of active rows                  | How many artifacts in this bag?        |
| `concatRepeater`     | Join text from each row                | `"Lithic, Ceramic, Glass, Bone"`       |
| `repeaterFieldAtRow` | Get a specific row's value             | First artifact's material type         |

#### Example: Artifact Bag Summary

A repeater section where each row is an artifact. Summary fields outside the repeater compute totals.

**Repeater rows:**

| Row | Type           | Weight |
| --- | -------------- | ------ |
| 1   | Lithic Flake   | 2.3 g  |
| 2   | Ceramic Sherd  | 5.1 g  |
| 3   | Glass Fragment | 1.8 g  |
| 4   | Shell Bead     | 0.4 g  |

**Summary fields outside the repeater:**

```
Total Count  = repeaterCount("Artifacts")              → 4
Total Weight = sumRepeater("Artifacts", "Weight")      → 9.6 g
Avg Weight   = avgRepeater("Artifacts", "Weight")      → 2.4 g
Materials    = concatRepeater("Artifacts", "Type", ", ")
             → "Lithic Flake, Ceramic Sherd, Glass Fragment, Shell Bead"
```

{% hint style="warning" %}
**Don't bake repeater section IDs into formulas.** Section IDs can be regenerated when an admin updates the archetype — use the section's **label** (display name) in repeater operators like `repeaterCount("Artifacts")` so formulas survive section edits.
{% endhint %}

***

## Child Record Aggregation

These operations look at child Records within the Project hierarchy and aggregate their data.

| Function             | Description                                                                            | Example                                     |
| -------------------- | -------------------------------------------------------------------------------------- | ------------------------------------------- |
| `countChildrenWhere` | Count children matching a condition (use a tautology condition to count all of a type) | How many Features have Status = "Complete"? |
| `childrenOfType`     | Get the array of child Records of a specific archetype                                 | List all Feature Records under this Site    |
| `sumChildren`        | Sum a field across child Records                                                       | Total artifact count across all units       |
| `hasChildOfType`     | Check if any children of an archetype exist                                            | Show summary only when children exist       |

{% hint style="warning" %}
**There is no bare `countChildren` operator.** To count all children of an archetype, use `countChildrenWhere(...)` with a condition that matches everything, or chain `count(childrenOfType("Feature"))` using the array `count` operator.
{% endhint %}

#### Example: Site-Level Roll-Up Summary

A Site Record automatically summarizes all its child Features and Artifacts.

```
Feature Count       = count(childrenOfType("Feature"))
                    → 12

Total Artifact Count = sumChildren("Feature", "Artifact Count")
                     → 347

Completed Features  = countChildrenWhere("Feature", "Status", equals "Complete")
                    → 8

Completion Rate     = round((Completed Features / Feature Count) × 100, 0) + "%"
                    → "67%"
```

{% hint style="info" %}
**Multi-level rollups need intermediate fields.** `sumChildren` and `countChildrenWhere` only see **direct** children. To roll up across two levels (e.g., grandchildren), put a numeric calculated field at the intermediate level (which sums *its* children), then sum the intermediates at the top.
{% endhint %}

***

## Building Dynamic Summaries

By combining text concatenation with aggregation and lookups, you can build rich, human-readable summaries that update automatically.

#### Example: Auto-Generated Excavation Unit Description

A comprehensive summary paragraph built entirely from formulas on an Excavation Unit Record.

```
Record Summary = concat(
  "This ", recordType, " was recorded on ",
  formatDate(startDate, "MM/DD/YYYY"), " by ", assigneeName, ". ",
  "It contains ", count(childrenOfType("Artifact")), " artifacts ",
  "across ", count(childrenOfType("Level")), " excavation levels. ",
  "The unit measures ", round(polygonArea(), 1), " square meters ",
  "and reaches a maximum depth of ",
  maxChildren("Level", "Depth"), " cmbs."
)
```

**Result:**

> "This Excavation Unit was recorded on 03/25/2026 by Maria Garcia. It contains 127 artifacts across 5 excavation levels. The unit measures 4.0 square meters and reaches a maximum depth of 95 cmbs."

***

## Tips for Aggregation Formulas

* **Aggregations update in real-time.** When you add a child Record, the parent's child-count and sum formulas update automatically. Delete a child and the counts adjust. No manual refresh needed.
* **Empty arrays return safe values.** Sum across zero children returns 0; concat across zero rows returns an empty string. You don't need defensive checks for "no children yet" in most cases.
* **Use `hasChildOfType` to gate visibility.** Combine it with a [visibility formula](/codifi-docs/cross-platform-features/ripple/visibility-formulas.md) to hide summary sections that would otherwise show "0 features, 0 artifacts" before any data has been collected.

***

## See Also

* [Value Formulas](/codifi-docs/cross-platform-features/ripple/value-formulas.md) — math, text, dates, and cross-Record lookups
* [Real-World Examples](/codifi-docs/cross-platform-features/ripple/examples.md#concatenated-record-summaries) — full site-summary and transect-summary configurations
* [Operations Reference](/codifi-docs/cross-platform-features/ripple/reference.md#aggregation) — every aggregation operator with arguments
