Query data in the Viam app
Explore and analyze your captured data using SQL or MQL queries. You can run queries interactively in the Viam app’s query editor or programmatically through the SDK for ad-hoc analysis, building dashboards, or integrating with your own tools.
Tabular data (sensor readings, motor positions, encoder ticks, and other structured key-value data) is queryable through SQL and MQL. Binary data (images, point clouds) is stored separately and accessible through the data client API.
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. Every SQL example and troubleshooting step on this page (including short inline examples) includes an explicit lower bound on time_received for this reason. When troubleshooting, do not remove the lower-bound filter entirely: widen it by using a broad lower bound such as AND time_received >= CAST('2000-01-01T00:00:00.000Z' AS TIMESTAMP). MQL queries are not affected. Tracked as APP-10891.
Open the query editor
- Go to app.viam.com.
- Click the DATA tab in the top navigation.
- Click Query to open the query editor.
- Select SQL or MQL mode depending on which language you want to use.
SQL is good for straightforward filtering, sorting, and limiting. MQL (MongoDB Query Language) uses aggregation pipelines and is more powerful for grouping, computing averages, and restructuring nested data.
By default, queries run against the readings collection in the sensorData database.
See Query reference for the full schema.
Explore your data with basic SQL
Start with a broad query to see what data you have:
SELECT time_received, component_name, component_type, data
FROM readings
WHERE time_received >= CAST('2000-01-01T00:00:00.000Z' AS TIMESTAMP)
ORDER BY time_received DESC
LIMIT 10
This shows the 10 most recent readings across all components.
Most of the interesting values are in the data column, which contains your actual readings as nested JSON.
To see the structure of your data, run this query for a specific component:
SELECT data FROM readings
WHERE component_name = 'my-sensor'
AND time_received >= CAST('2000-01-01T00:00:00.000Z' AS TIMESTAMP)
LIMIT 1
Switch to table view (the table icon in the results area) to see nested fields automatically flattened into dot-notation column headers like data.readings.temperature. These dot-notation paths are exactly what you use in your queries to extract specific values.
For the full schema and per-component examples, see the readings table schema.
To narrow to a specific component:
SELECT time_received, data
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
To filter by time range:
SELECT time_received, component_name, data
FROM readings
WHERE time_received > '2025-01-15T00:00:00Z'
AND time_received < '2025-01-16T00:00:00Z'
ORDER BY time_received ASC
Extract fields from nested JSON
The data column contains JSON, so you need JSON functions to extract specific
values. Use dot notation to reach into the nested structure:
SELECT
time_received,
data.readings.temperature AS temperature,
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 20
For detection results from a vision service:
SELECT
time_received,
data.detections
FROM readings
WHERE component_name = 'my-detector'
AND component_type = 'rdk:service:vision'
AND time_received >= CAST('2000-01-01T00:00:00.000Z' AS TIMESTAMP)
ORDER BY time_received DESC
LIMIT 10
To filter by a specific machine:
SELECT time_received, component_name, data
FROM readings
WHERE robot_id = 'YOUR-MACHINE-ID'
AND time_received >= CAST('2000-01-01T00:00:00.000Z' AS TIMESTAMP)
ORDER BY time_received DESC
LIMIT 10
Write MQL aggregation pipelines
Switch to MQL mode in the query editor. MQL queries are JSON arrays where each element is a pipeline stage.
Get the last 10 readings from a component:
[
{ "$match": { "component_name": "my-sensor" } },
{ "$sort": { "time_received": -1 } },
{ "$limit": 10 },
{
"$project": {
"time_received": 1,
"data": 1,
"_id": 0
}
}
]
Count readings per component:
[
{
"$group": {
"_id": "$component_name",
"count": { "$sum": 1 }
}
},
{ "$sort": { "count": -1 } }
]
Count readings per component over a specific time window:
[
{
"$match": {
"time_received": {
"$gte": { "$date": "2025-01-15T00:00:00Z" },
"$lt": { "$date": "2025-01-16T00:00:00Z" }
}
}
},
{
"$group": {
"_id": "$component_name",
"count": { "$sum": 1 }
}
},
{ "$sort": { "count": -1 } }
]
Compute average, min, and max of a sensor value:
[
{ "$match": { "component_name": "my-sensor" } },
{
"$group": {
"_id": null,
"avg_temperature": { "$avg": "$data.readings.temperature" },
"min_temperature": { "$min": "$data.readings.temperature" },
"max_temperature": { "$max": "$data.readings.temperature" },
"total_readings": { "$sum": 1 }
}
}
]
Group readings by hour to see trends over time:
[
{ "$match": { "component_name": "my-sensor" } },
{
"$group": {
"_id": {
"$dateToString": {
"format": "%Y-%m-%d %H:00",
"date": "$time_received"
}
},
"avg_temperature": { "$avg": "$data.readings.temperature" },
"count": { "$sum": 1 }
}
},
{ "$sort": { "_id": 1 } }
]
Find all detections above a confidence threshold:
[
{ "$match": { "component_name": "my-detector" } },
{ "$unwind": "$data.detections" },
{ "$match": { "data.detections.confidence": { "$gte": 0.9 } } },
{
"$project": {
"time_received": 1,
"class_name": "$data.detections.class_name",
"confidence": "$data.detections.confidence",
"_id": 0
}
},
{ "$sort": { "time_received": -1 } },
{ "$limit": 20 }
]
The $unwind stage is important when your data contains arrays. It flattens
the array so each element becomes its own document, which you can then filter
and project individually.
Query from code
You can run the same SQL and MQL queries from Python or Go using the data client API. See Query data from code for setup instructions and examples.
Try it
To get oriented with your own data:
Open the query editor and 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 a component and run the following to see the JSON structure of its readings:
SELECT data FROM readings WHERE component_name = 'YOUR-COMPONENT' AND time_received >= CAST('2000-01-01T00:00:00.000Z' AS TIMESTAMP) LIMIT 1Use the field names from step 2 to write a query that extracts a specific value with dot notation (for example,
data.readings.temperature).
For the full schema of the readings table, see Query reference.
Troubleshooting
What’s next
- Create a data pipeline: create precomputed summaries for faster queries.
- Hot data store: query a rolling window of recent data with lower latency.
- Sync data to your database: export data to your own MongoDB instance.
- Visualize data: build dashboards from your captured data.
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!