BonnardBonnard0.3.7

Query API Reference

Complete reference for querying the Bonnard semantic layer via the SDK.

query()

Execute a JSON query against the semantic layer. All field names must be fully qualified (e.g. orders.revenue, not revenue).

const { data } = await bon.query({
  measures: ['orders.revenue', 'orders.count'],
  dimensions: ['orders.city'],
  filters: [
    { dimension: 'orders.status', operator: 'equals', values: ['completed'] },
  ],
  timeDimension: {
    dimension: 'orders.created_at',
    granularity: 'month',
    dateRange: ['2025-01-01', '2025-12-31'],
  },
  orderBy: { 'orders.revenue': 'desc' },
  limit: 10,
});

Parameters

ParameterTypeRequiredDescription
measuresstring[]NoNumeric aggregations to compute (e.g. ['orders.revenue'])
dimensionsstring[]NoGroup-by columns (e.g. ['orders.city'])
filtersFilter[]NoRow-level filters
timeDimensionTimeDimensionNoTime-based grouping and date range
orderByRecord<string, 'asc' | 'desc'>NoSort order
limitnumberNoMax rows to return

At least one of measures or dimensions is required.

Response

{
  data: [
    { "orders.revenue": 125000, "orders.count": 340, "orders.city": "Berlin" },
    { "orders.revenue": 98000, "orders.count": 280, "orders.city": "Munich" },
  ],
  annotation: {
    measures: {
      "orders.revenue": { title: "Revenue", type: "number" },
      "orders.count": { title: "Count", type: "number" },
    },
    dimensions: {
      "orders.city": { title: "City", type: "string" },
    },
  }
}
  • data — array of result rows, keyed by fully qualified field names
  • annotation — optional metadata with field titles and types

Filters

filters: [
  { dimension: 'orders.status', operator: 'equals', values: ['completed'] },
  { dimension: 'orders.revenue', operator: 'gt', values: [1000] },
]

Filter operators

OperatorDescriptionExample values
equalsExact match (any of values)['completed', 'shipped']
notEqualsExclude matches['cancelled']
containsSubstring match['berlin']
gtGreater than[1000]
gteGreater than or equal[1000]
ltLess than[100]
lteLess than or equal[100]

Time dimensions

Group data by time periods and filter by date range.

timeDimension: {
  dimension: 'orders.created_at',
  granularity: 'month',
  dateRange: ['2025-01-01', '2025-06-30'],
}

Granularities

GranularityGroups by
dayCalendar day
weekISO week (Monday start)
monthCalendar month
quarterCalendar quarter
yearCalendar year

Omit granularity to filter by date range without time grouping.

Date range formats

// ISO date strings (tuple)
dateRange: ['2025-01-01', '2025-12-31']

// Single string (relative)
dateRange: 'last 30 days'
dateRange: 'last 6 months'
dateRange: 'this year'
dateRange: 'last year'
dateRange: 'today'
dateRange: 'yesterday'
dateRange: 'last week'
dateRange: 'last month'
dateRange: 'last quarter'

Ordering and pagination

// Sort by revenue descending
orderBy: { 'orders.revenue': 'desc' }

// Multiple sort keys
orderBy: { 'orders.city': 'asc', 'orders.revenue': 'desc' }

// Limit results
limit: 10

rawQuery()

Pass a Cube-native query object directly, bypassing the SDK's query format conversion. Use when you need features not exposed by query() (e.g. segments, offset).

const { data } = await bon.rawQuery({
  measures: ['orders.revenue'],
  dimensions: ['orders.city'],
  order: [['orders.revenue', 'desc']],
  limit: 10,
  offset: 20,
});

sql()

Execute a SQL query using Cube's SQL API. Use MEASURE() to reference semantic layer measures.

const { data } = await bon.sql(
  `SELECT city, MEASURE(revenue), MEASURE(count)
   FROM orders
   WHERE status = 'completed'
   GROUP BY 1
   ORDER BY 2 DESC
   LIMIT 10`
);

Response includes an optional schema:

{
  data: [
    { city: "Berlin", revenue: 125000, count: 340 },
  ],
  schema: [
    { name: "city", type: "string" },
    { name: "revenue", type: "number" },
    { name: "count", type: "number" },
  ]
}

explore()

Discover available views, measures, dimensions, and segments.

const meta = await bon.explore();

for (const view of meta.cubes) {
  console.log(`${view.name}: ${view.description || ''}`);
  for (const m of view.measures) {
    console.log(`  measure: ${m.name} (${m.type})`);
  }
  for (const d of view.dimensions) {
    console.log(`  dimension: ${d.name} (${d.type})`);
  }
}

By default returns only views (viewsOnly: true). To include underlying cubes:

const meta = await bon.explore({ viewsOnly: false });

Response shape

{
  cubes: [
    {
      name: "orders",
      title: "Orders",
      description: "Customer orders",
      type: "view",
      measures: [
        { name: "orders.revenue", title: "Revenue", type: "number" },
        { name: "orders.count", title: "Count", type: "number" },
      ],
      dimensions: [
        { name: "orders.city", title: "City", type: "string" },
        { name: "orders.created_at", title: "Created At", type: "time" },
      ],
      segments: [],
    }
  ]
}

toCubeQuery()

Utility function that converts SDK QueryOptions into a Cube-native query object. Useful for debugging or when you need to inspect the query before sending.

import { toCubeQuery } from '@bonnard/sdk';
// or in browser: Bonnard.toCubeQuery(...)

const cubeQuery = Bonnard.toCubeQuery({
  measures: ['orders.revenue'],
  dimensions: ['orders.city'],
  orderBy: { 'orders.revenue': 'desc' },
});

console.log(JSON.stringify(cubeQuery, null, 2));
// {
//   "measures": ["orders.revenue"],
//   "dimensions": ["orders.city"],
//   "order": [["orders.revenue", "desc"]]
// }

Common patterns

KPI query (single row, no dimensions)

const { data } = await bon.query({
  measures: ['orders.revenue', 'orders.count', 'orders.avg_value'],
});
const kpis = data[0];
// { "orders.revenue": 1250000, "orders.count": 3400, "orders.avg_value": 367.6 }

Top N with dimension

const { data } = await bon.query({
  measures: ['orders.revenue'],
  dimensions: ['orders.city'],
  orderBy: { 'orders.revenue': 'desc' },
  limit: 10,
});

Time series

const { data } = await bon.query({
  measures: ['orders.revenue'],
  timeDimension: {
    dimension: 'orders.created_at',
    granularity: 'month',
    dateRange: 'last 12 months',
  },
});
// data[0]["orders.created_at"] is an ISO date string like "2025-01-01T00:00:00.000"

Filtered breakdown

const { data } = await bon.query({
  measures: ['orders.revenue'],
  dimensions: ['orders.product_category'],
  filters: [
    { dimension: 'orders.city', operator: 'equals', values: ['Berlin'] },
    { dimension: 'orders.revenue', operator: 'gt', values: [100] },
  ],
  orderBy: { 'orders.revenue': 'desc' },
});

See also

On this page