# Aggregate API (generated)

> Run a MongoDB aggregation pipeline through the generated aggregate API of API Maker - $match, $group, $project, $addFields, $bucket, $facet, $lookup, $count and the other stages, with the permissions of the caller.

Source: https://docs.apimaker.dev/v1/docs/apis-all/generated-apis/auto-generated-aggregate-api.html

Runs a MongoDB aggregation pipeline on a collection : the body is the array of stages, the answer their result.

| | |
|---|---|
| Method | POST |
| URL | `/api/gen/admin/mongodb/inventory/customers/aggregate` |
| Body | the pipeline : an array of stages |
| Query params | none |
| Answer | `data` : the documents produced by the pipeline |
| Databases | MongoDB only |
| Cached | yes, with `enableCaching` on the table |
| From code | [`g.sys.db.gen.aggregateGen`](/v1/examples/sys/db/gen/aggregateGen.html) |
| API id | `GEN_POST_AGGREGATE` (groups, settings, hooks, WebSocket subscriptions) |
| The other family | [Aggregate as a schema API](/v1/docs/apis-all/schema-apis/auto-generated-schema-based-aggregate-api.html) |

!!! info "Schemaless : the data passes as it is"
    The generated APIs never read the [schema](/v1/docs/schema/schema.html) of the table. Nothing is converted, validated, defaulted, encrypted or numbered : what you send is what the database gets, and a filter matches the type you send (`"22"` does not match the number `22`). On MongoDB, strings which look like ObjectIds are converted. Every table has these APIs, with or without a schema ; the same operations under `/api/schema` apply the schema.

## The URL

`/api/gen/admin/mongodb/inventory/customers/aggregate` : replace `admin` with the user path of your account, `mongodb` with your instance, `inventory` with the database and `customers` with the table. The examples use this table :

| customer_id | first_name | last_name | last_update           | pincode | isActive |
|-------------|------------|-----------|-----------------------|---------|----------|
| 1           | Bob        | lin       | 2022-11-14 04: 34: 58 | 382345  | 1        |
| 2           | Alice      | Page      | 2022-10-15 02: 10: 40 | 382346  | 1        |
| 3           | Mallory    | Brown     | 2022-09-13 03: 44: 05 | 382347  | 1        |
| 4           | Eve        | Mathly    | 2022-11-12 01: 59: 33 | 382348  | 1        |
| 5           | Eve        | Page      | 2022-11-12 01: 59: 33 | 382349  | 1        |

## The call

```text
POST /api/gen/admin/mongodb/inventory/customers/aggregate
```

```json title="Body : orders per customer, the biggest first"
[
    { "$match": { "status": "PAID" } },
    { "$group": { "_id": "$customer_id", "orders": { "$sum": 1 }, "total": { "$sum": "$total" } } },
    { "$sort": { "total": -1 } },
    { "$limit": 10 }
]
```

```json title="Answer"
{ "success": true, "statusCode": 200, "data": [ { "_id": 1, "orders": 4, "total": 51600 } ] }
```

- A body which is not an array is refused with `Please provide array for aggregate body.`

## Stages

Every stage of the MongoDB version you run is accepted. The ones used most :

| Stage | Example |
|---|---|
| `$match` | `{ "$match": { "pincode": { "$gt": 380000 } } }` |
| `$group` | `{ "$group": { "_id": "$pincode", "count": { "$sum": 1 } } }` |
| `$project` | `{ "$project": { "name": { "$toLower": "$first_name" } } }` |
| `$addFields` | `{ "$addFields": { "full_name": { "$concat": [ "$first_name", " ", "$last_name" ] } } }` |
| `$sort`, `$skip`, `$limit` | `{ "$sort": { "total": -1 } }` |
| `$count` | `{ "$count": "customers" }` |
| `$bucket` | `{ "$bucket": { "groupBy": "$price", "boundaries": [ 0, 100, 500 ], "default": "Other", "output": { "count": { "$sum": 1 } } } }` |
| `$facet` | several pipelines in one : `{ "$facet": { "byPrice": [ … ], "byCategory": [ … ] } }` |
| `$lookup` | a join inside MongoDB : `{ "$lookup": { "from": "orders", "localField": "customer_id", "foreignField": "customer_id", "as": "orders" } }` |
| `$unwind` | `{ "$unwind": "$orders" }` |
| `$collStats` | `{ "$collStats": { "storageStats": {}, "count": {} } }` |

## Good to know

- Only MongoDB : SQL databases answer `This API is only supported for mongodb.` For SQL, run a query with [executeQuery](/v1/examples/sys/system/executeQuery.html) from a custom API.
- Field permissions of the groups apply to what a stage returns. A caller who can not read a field does not get it, projected or not.
- With caching on, a pipeline answers from Redis until the collection changes.

## From your code

[`g.sys.db.gen.aggregateGen`](/v1/examples/sys/db/gen/aggregateGen.html) makes the same call from custom APIs, hooks, events, schedulers and test cases, with the same headers and params. Pass `true` as the second argument to get the whole [response envelope](/v1/docs/apis-all/response-format.html) instead of `data`.

## Headers

Every call takes the [request headers](/v1/docs/apis-all/header/requestHeader.html) : the tokens, `x-am-response-case`, `x-am-content-type-response` (JSON, XML, YAML…), `x-am-response-object-type: make_flat`, `x-am-cache-control`, `x-am-get-encrypted-data`, `x-am-internationalization`, `x-am-tenant-username`, `x-am-meta`.

## Errors

| Code | When |
|---|---|
| `401` | The token in `x-am-authorization` is missing or invalid. |
| `403` | No group of the API user grants this API of this table, or a field of the request. |
| `404` | The instance, database or table of the URL does not exist. |
| `400` | The query or the body is wrong : the [error messages](/v1/docs/apis-all/error-codes.html) say which key and why. |

## Related

- [All APIs at a glance](/v1/docs/apis-all/overview.html) · [Query params](/v1/docs/apis-all/query-params/query-params.html) · [Response format](/v1/docs/apis-all/response-format.html) · [Pre hooks](/v1/docs/apis-all/hooks/preHook-api.html) and [post hooks](/v1/docs/apis-all/hooks/postHook-api.html) · [Automatic caching](/v1/docs/features/automatic-caching.html)
