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 readings table directly.
  • Viam SDK: the data client’s tabular_data_by_sql and tabular_data_by_mql methods 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

ColumnTypeDescription
organization_idStringOrganization UUID. Set automatically from your machine’s config.
location_idStringLocation UUID. The location this machine belongs to.
robot_idStringMachine UUID. Identifies which machine captured this data.
part_idStringMachine part UUID. Identifies which part of the machine.
component_typeStringResource type as a triplet (for example, rdk:component:sensor).
component_nameStringThe name you gave this component in your config (for example, my-sensor). This is what you filter on most often.
method_nameStringThe capture method (for example, Readings, GetImages, EndPosition).
time_requestedTimestampWhen the capture was requested on the machine (machine’s clock).
time_receivedTimestampWhen the cloud received and stored the data. Use this for time-range queries since it’s indexed.
tagsArrayUser-applied tags from your data management config.
additional_parametersJSONMethod-specific parameters you configured (for example, pin_name, reader_name).
dataJSONYour actual captured values. Structure varies by component type and method.

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.

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:

  1. 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)
    
  2. 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 1
    
  3. Look at the JSON structure in the result. The keys you see are the fields you can query with dot notation.

  4. Build your query using data. followed by the path to the field you want (for example, data.readings.temperature).