Bonnard Docs

Query

Query the deployed semantic layer using JSON or SQL.

Overview

After deploying with bon deploy, you can query the semantic layer using bon query. This tests that your cubes and views work correctly and returns data from your warehouse through Bonnard.

Query Formats

Bonnard supports two query formats:

JSON Format (Default)

The JSON format uses the REST API structure:

bon query '\{"measures": ["orders.count"]\}'

bon query '\{
  "measures": ["orders.total_revenue"],
  "dimensions": ["orders.status"],
  "filters": [\{
    "member": "orders.created_at",
    "operator": "inDateRange",
    "values": ["2024-01-01", "2024-12-31"]
  \}]
\}'

JSON Query Properties:

PropertyDescription
measuresArray of measures to calculate (e.g., ["orders.count"])
dimensionsArray of dimensions to group by (e.g., ["orders.status"])
filtersArray of filter objects
timeDimensionsTime-based grouping with granularity
segmentsNamed filters defined in cubes
limitMax rows to return
offsetSkip rows (pagination)
orderSort specification

Filter Operators:

OperatorUse Case
equals, notEqualsExact match
contains, notContainsString contains
startsWith, endsWithString prefix/suffix
gt, gte, lt, lteNumeric comparison
inDateRange, beforeDate, afterDateTime filtering
set, notSetNULL checks

SQL Format

The SQL format uses the SQL API, where cubes are tables:

bon query --sql "SELECT status, MEASURE(count) FROM orders GROUP BY 1"

bon query --sql "SELECT
  city,
  MEASURE(total_revenue),
  MEASURE(avg_order_value)
FROM orders
WHERE status = 'completed'
GROUP BY 1
ORDER BY 2 DESC
LIMIT 10"

SQL Syntax Rules:

  1. Cubes/views are tablesFROM orders references the orders cube
  2. Dimensions are columns — Include in SELECT and GROUP BY
  3. Measures use MEASURE() — Or matching aggregates (SUM, COUNT, etc.)
  4. Segments are boolean — Filter with WHERE is_completed IS TRUE

Examples:

-- Count orders by status
SELECT status, MEASURE(count) FROM orders GROUP BY 1

-- Revenue by city with filter
SELECT city, SUM(amount) FROM orders WHERE status = 'shipped' GROUP BY 1

-- Using time dimension with granularity
SELECT DATE_TRUNC('month', created_at), MEASURE(total_revenue)
FROM orders
GROUP BY 1
ORDER BY 1

CLI Usage

# JSON format (default)
bon query '\{"measures": ["orders.count"]\}'

# SQL format
bon query --sql "SELECT MEASURE(count) FROM orders"

# Limit rows
bon query '\{"measures": ["orders.count"], "dimensions": ["orders.city"]\}' --limit 10

# JSON output (instead of table)
bon query '\{"measures": ["orders.count"]\}' --format json

Output Formats

Table Format (Default)

┌─────────┬───────────────┐
│ status  │ orders.count  │
├─────────┼───────────────┤
│ pending │ 42            │
│ shipped │ 156           │
│ done    │ 892           │
└─────────┴───────────────┘

JSON Format

bon query '\{"measures": ["orders.count"]\}' --format json
[
  \{ "orders.status": "pending", "orders.count": 42 \},
  \{ "orders.status": "shipped", "orders.count": 156 \},
  \{ "orders.status": "done", "orders.count": 892 \}
]

Common Patterns

Time Series Analysis

bon query '\{
  "measures": ["orders.total_revenue"],
  "timeDimensions": [\{
    "dimension": "orders.created_at",
    "granularity": "month",
    "dateRange": ["2024-01-01", "2024-12-31"]
  \}]
\}'

Filtering by Dimension

bon query '\{
  "measures": ["orders.count"],
  "dimensions": ["orders.city"],
  "filters": [\{
    "member": "orders.status",
    "operator": "equals",
    "values": ["completed"]
  \}]
\}'

Multiple Measures

bon query '\{
  "measures": ["orders.count", "orders.total_revenue", "orders.avg_order_value"],
  "dimensions": ["orders.category"]
\}'

Error Handling

Common Errors

"Projection references non-aggregate values"

  • All dimensions must be in GROUP BY
  • All measures must use MEASURE() or matching aggregate

"Cube not found"

  • Check cube name matches deployed cube
  • Run bon deploy if cubes changed

"Not logged in"

  • Run bon login first

See Also