= Aggregations :nav-title: Aggregations :description: Reports, counts and grouped data computed by MongoDB: define a pipeline once in the console, test it, and call it from your app by name. :keywords: MongoDB aggregation, aggregation pipeline, reporting API, group by, analytics endpoint, server-side query :group: Your data :order: 30 Sales by region, orders by status, the ten most active users: that is an aggregation, a pipeline of MongoDB stages that MongoDB runs for you. You define it once under **Aggregations**, give it a name, and your app calls it by that name. The app never sends a query, so it can only run what you approved, and the logic lives in one place for every client. image::/assets/docs-images/aggregations.png[The Aggregations page, with a pipeline being added to a collection] == Define one . Open **Aggregations** and expand the collection. . Click **Add Aggregation**. . Give it a **uri**: the name it will be called by, unique in the collection. `sales-by-region`, `count-by-status`. . Write the **stages**: the pipeline, as a JSON array. . **Test**, beside **Create** or **Save**, shows what it returns; then save it. [source,json] ---- [ { "$match": { "status": "completed" } }, { "$group": { "_id": "$region", "total": { "$sum": "$amount" } } }, { "$sort": { "total": -1 } } ] ---- The editor helps: **Add template** starts you off, **Add pagination** appends `$skip` and `$limit` stages driven by parameters, and **Format** tidies the JSON and points at a mistake. **Test** executes the pipeline against the collection as it stands, before you save, with the service's own security checks. It refuses `$out` and `$merge`, so it can look but not write. Saving takes effect at once. The `uri` cannot be changed afterwards, since it is part of the address your app calls; the stages can. == Rerank the results With a reranking model set under xref:ai-keys.adoc[AI Keys], the form has a **Rerank** section: tick it, name the query variable (`q` by default, the parameter that carries the question) and, if you want, how many results to keep. The service hands what the pipeline returns to the model, which reorders it by how well each result answers the question. The model reads each result's `text` field, or the whole document when there is none, so add a `$set` stage that copies the field to judge by into `text`. See xref:ai-keys.adoc#reranking-a-search[Reranking a search]. == Call it from your app [source] ---- GET //_aggrs/?page=1&pagesize=20 ---- [source,javascript] ---- const res = await fetch('https://c0ffee.ulabase.app/orders/_aggrs/sales-by-region', { headers: { Authorization: 'Basic ' + btoa('alice:secret') } }); const rows = await res.json(); ---- The user needs a permission that allows `GET` on that path, like any other read. Results are paged with `page` and `pagesize`. == Parameters [[avars]] A pipeline can read values from the request with `$var`, so one definition serves many questions: [source,json] ---- [ { "$match": { "region": { "$var": "region" }, "amount": { "$gt": { "$var": "minAmount" } } } }, { "$count": "total" } ] ---- The app passes each one as a query parameter of the same name: [source,javascript] ---- const res = await fetch('https://c0ffee.ulabase.app/orders/_aggrs/sales-by-region?region=europe&minAmount=1000'); ---- A value that looks like JSON is read as JSON: `?tags=[1,2,3]` binds an array, `?opts={"limit":10}` an object. Anything else is taken as a string, so a bare word needs no quoting. One kind of variable cannot travel this way: the one whose name is a query parameter the service reads itself, `page`, `sort`, `filter`, `keys` and the like, which would never reach the pipeline. For those, and for a service older than 9.9, there is the older form: one `avars` object holding them all. [source,javascript] ---- const avars = encodeURIComponent(JSON.stringify({ sort: { date: -1 } })); const res = await fetch(`https://c0ffee.ulabase.app/orders/_aggrs/sales-by-region?avars=${avars}`); ---- Both forms work together in one request, and `avars` wins if it carries the same name. In the editor, when the pipeline uses `$var`, a **Parameters for the run** box appears. **Fill in the ones this pipeline uses** writes the skeleton with every name the pipeline reads. `$ifvar` includes a stage only when a parameter is present; the editor's **Variables and optional stages** panel shows both with the predefined names. == Large pipelines MongoDB gives a pipeline 100 MB of memory. A `$group` or `$sort` over millions of documents can exceed it and fail. Turn on **allowDiskUse** for that pipeline and MongoDB spills to disk instead. Before you do, add an xref:managing-indexes.adoc[index] on the fields your `$match` and `$sort` stages use: it usually removes the need. == From a script Aggregations are stored in the collection's metadata, in the `aggrs` array, so a script writes them with the rest of the collection's settings: [cols="2,3"] |=== | Operation | API | Read the definitions | `GET //_meta` | Save them | `PATCH /` with `{ "aggrs": [ { "uri": "...", "stages": [...], "allowDiskUse": false } ] }` | Run one | `GET //_aggrs/?=&page=1&pagesize=20` |=== With xref:cli.adoc[`ulabase`], they are part of the setup file. == Related pages * xref:constraints.adoc[Constraints]: the same pipeline editor, used to write rules a write cannot break. * xref:mcp.adoc[MCP server]: an aggregation you publish becomes a tool an AI agent can call. * xref:managing-permissions.adoc[Permissions]: who may call which aggregation.