# Data retrieval

Everything OpenGate stores about your fleet is queryable through one consistent mechanism: **`POST` a JSON
query, get rows back**. There is no query string to assemble and no SQL to learn — the resource lives in the
URL, and the conditions live in a JSON body.

## How a query is built

```mermaid
flowchart LR
    URL["<b>The URL</b><br>what you are querying<br>/north/v80/search/devices"] --> REQ(["POST"])
    BODY["<b>The JSON body</b><br>which rows you want<br>filter, select, sort, group, limit"] --> REQ
    REQ --> RES["<b>Rows</b><br>JSON or CSV"]
```

Two things to learn, and this section is organized around exactly that:

1. **[What you can query](what_you_can_query/)** — the index of every search endpoint, so you know which URL
   to `POST` to.
2. **[Data Lake](data_lake/)** — the query language: `filter`, `select`, `sort`, `group` and `limit`.

Then, because three kinds of storage answer slightly differently, **[Query dialects](query_dialects/)**
lays their differences side by side.

## Your first query in 60 seconds

Ask for your devices. No filter, no options — just the resource:

```bash
curl --request POST \
     --header "X-ApiKey: <your-api-key>" \
     --header "Content-Type: application/json" \
     --data '{}' \
     https://api.opengate.es/north/v80/search/devices
```

You get an array named after the resource. Each row is a flat map of **dotted field paths**, and each value
is wrapped in `_value._current.value` — the same shape the platform uses to hold a current value and its
metadata:

```json
{
  "devices": [
    {
      "provision.administration.identifier": {
        "_value": { "_current": { "value": "device_battery_id" } }
      },
      "provision.administration.channel": {
        "_value": { "_current": { "value": "battery_channel" } }
      },
      "provision.device.location": {
        "_value": { "_current": { "value": {
          "position": { "type": "Point", "coordinates": [-3.7028, 40.41675] },
          "postal": "28013"
        } } }
      }
    }
  ],
  "page": { "number": 1 }
}
```

Those dotted paths are also the field names you filter and sort on. Narrow the query with a `filter` tree
and page it with `limit`:

```json
{
  "filter": {
    "and": [
      { "like": { "provision.device.administrativeState": "NORMAL" } },
      { "like": { "provision.device.communicationModules[].mobile.imei": "351873000102290" } }
    ]
  },
  "limit": { "start": 1, "size": 50 }
}
```

{{% notice style="info" title="Discovering field names" icon="lightbulb" %}}
Run the query with an empty body first. The paths you see in the response are exactly the paths you can
filter, sort and select on — which is the fastest way to learn any resource's fields.
{{% /notice %}}

Swap `devices` in the URL for `datapoints`, `entities/alarms` or any resource from
[What you can query](what_you_can_query/), and the same body shape applies — only the field names change.

## Reading the answer

| You want | Use | Where |
|---|---|---|
| Rows | The search endpoint | [Data Lake](data_lake/) |
| How many, not which | The same endpoint with `/summary` | [Summary](data_lake/features/summary/) |
| Only some fields | `select` | [Selecting](data_lake/features/selecting/) |
| Rows in pages | `limit` | [Pagination](data_lake/features/pagination/) |
| Rows grouped and aggregated | `group` | [Grouping](data_lake/features/grouping/) |
| A CSV file instead of JSON | An HTTP header option | [Query dialects](query_dialects/) |

## What each store is for

| Store | Holds | Query it when |
|---|---|---|
| **[Data streams](data_streams/)** | The current value of each measurement | You want the latest reading, not the history |
| **[Alarms](alarms/)** | Alarms raised on entities | You are monitoring what went wrong |
| **[Time series](time_series/)** | Values pre-aggregated into fixed time buckets per device | You want history at scale: hourly or daily aggregates already computed |
| **[Data sets](data_sets/)** | A tabular projection of chosen data streams | You want a flat table, typically to export as CSV |
| **[Notebook scheduler](opengate_data_notebook_scheduler/)** | Notebooks run unattended, once or on a schedule | Your analysis is code, not a query |
| **[Data points](data_points/)** | Every raw value a device ever sent | **Deprecated.** Superseded by time series |

{{% children sort="weight" depth="10" %}}
