Captured data schema
When you enable data capture on a component, viam-server records its readings to local disk and the data management service syncs those recordings to the cloud. Once synced, the captured tabular data (sensor readings, motor positions, encoder ticks, and other structured key-value data) lands in a single table called readings in the sensorData database. Each row represents one capture event from one resource.
You can access this data in three ways:
- Viam app: the DATA tab shows latest readings on the Sensors tab. The Query editor lets you run SQL or MQL against the
readingstable directly. - Viam SDK: the data client’s
tabular_data_by_sqlandtabular_data_by_mqlmethods query the same table from Python or Go code. - External tools: connect Grafana, Tableau, or any MongoDB client using the database credentials from
viam data database configure. See Visualize data.
This page describes the structure of the readings table and the nested data column so you know what to query.
For query syntax, optimization tips, and supported operators, see Data reference.
What a document looks like
Every row in the readings table has two layers: metadata that Viam adds automatically (organization, location, machine, part, component, method, timestamps, tags) and your actual captured values inside the data column.
Here is a complete row for a sensor called my-sensor:
{
"organization_id": "ab1c2d3e-1234-5678-abcd-ef1234567890",
"location_id": "loc-1234-5678-abcd-ef1234567890",
"robot_id": "robot-1234-5678-abcd-ef1234567890",
"part_id": "part-1234-5678-abcd-ef1234567890",
"component_type": "rdk:component:sensor",
"component_name": "my-sensor",
"method_name": "Readings",
"time_requested": "2025-03-15T14:30:00.000Z",
"time_received": "2025-03-15T14:30:01.234Z",
"tags": ["production", "floor-2"],
"additional_parameters": {},
"data": {
"readings": {
"temperature": 23.5,
"humidity": 61.2
}
}
}
The metadata fields (organization_id through additional_parameters) are the same structure for every component type. The data column is different for each component and capture method. To query your specific values, you use dot notation into data (for example, data.readings.temperature).
Column reference
| Column | Type | Description |
|---|---|---|
organization_id | String | Organization UUID. Set automatically from your machine’s config. |
location_id | String | Location UUID. The location this machine belongs to. |
robot_id | String | Machine UUID. Identifies which machine captured this data. |
part_id | String | Machine part UUID. Identifies which part of the machine. |
component_type | String | Resource type as a triplet (for example, rdk:component:sensor). |
component_name | String | The name you gave this component in your config (for example, my-sensor). This is what you filter on most often. |
method_name | String | The capture method (for example, Readings, GetImages, EndPosition). |
time_requested | Timestamp | When the capture was requested on the machine (machine’s clock). |
time_received | Timestamp | When the cloud received and stored the data. Use this for time-range queries since it’s indexed. |
tags | Array | User-applied tags from your data management config. |
additional_parameters | JSON | Method-specific parameters you configured (for example, pin_name, reader_name). |
data | JSON | Your actual captured values. Structure varies by component type and method. |
Note
The readings table does not include robot_name or part_name columns. These fields appear in data export responses but are not part of the queryable schema. To filter by machine, use robot_id or part_id.
The data column
The data column contains your actual captured values as nested JSON. Its structure depends on what component and method captured the data. To find out what’s inside data for your specific components, run:
SELECT data FROM readings
WHERE component_name = 'my-sensor'
AND time_received >= CAST('2000-01-01T00:00:00.000Z' AS TIMESTAMP)
LIMIT 1
Then use the field names you see to build more specific queries.
Known issue: SQL queries need an explicit lower time bound
SQL queries against readings currently return no rows unless the WHERE clause includes an explicit lower bound on time_received. Include AND time_received >= CAST('2000-01-01T00:00:00.000Z' AS TIMESTAMP) in any SQL example on this page if you copy it. MQL queries are not affected. Tracked as APP-10891.
Common data structures:
Keys inside data.readings are whatever your sensor returns. Each sensor is different.
{
"readings": {
"temperature": 23.5,
"humidity": 61.2
}
}
To query: data.readings.temperature
Example:
SELECT time_received,
data.readings.temperature AS temp,
data.readings.humidity AS humidity
FROM readings
WHERE component_name = 'my-sensor'
AND time_received >= CAST('2000-01-01T00:00:00.000Z' AS TIMESTAMP)
ORDER BY time_received DESC
LIMIT 10
Detections include bounding box coordinates and confidence scores.
{
"detections": [
{
"class_name": "person",
"confidence": 0.94,
"x_min": 120,
"y_min": 50,
"x_max": 340,
"y_max": 480
}
]
}
To query detections, use MQL since SQL cannot easily traverse arrays:
[
{ "$match": { "component_name": "my-vision" } },
{ "$unwind": "$data.detections" },
{ "$match": { "data.detections.confidence": { "$gt": 0.8 } } },
{
"$project": {
"class": "$data.detections.class_name",
"confidence": "$data.detections.confidence",
"time": "$time_received"
}
}
]
{
"position": 145.7
}
To query: data.position
{
"position": 12450,
"position_type": 1
}
To query: data.position
Finding the structure of your data
If you’re unsure what fields your component produces:
Run the following to see what components have captured data:
SELECT DISTINCT component_name FROM readings WHERE time_received >= CAST('2000-01-01T00:00:00.000Z' AS TIMESTAMP)Pick one and run the following:
SELECT data FROM readings WHERE component_name = 'YOUR-COMPONENT' AND time_received >= CAST('2000-01-01T00:00:00.000Z' AS TIMESTAMP) LIMIT 1Look at the JSON structure in the result. The keys you see are the fields you can query with dot notation.
Build your query using
data.followed by the path to the field you want (for example,data.readings.temperature).
Was this page helpful?
Glad to hear it! If you have any other feedback please let us know:
We're sorry about that. To help us improve, please tell us what we can do better:
Thank you!