Metrics
A
metric is a measure of the semantic model, fully qualified:
. The app's
Metrics page lists every measure of the
cubes built from data sources, and a metric's own page lets you count it over
a
scope (a segment, or a few conditions) and
break it down by a
dimension — the same three settings a dashboard card stores. This skill reads
only; nothing here writes.
For ad-hoc analytical questions that aren't about one metric — several
measures at once, custom grouping, top-N across cubes — the
semantic-model skill is the better fit. Use this one when the user
thinks in terms of a named metric, or wants the number a card shows.
The catalogue
json
api_read { "resource": "cubes", "method": "meta", "params": { "fields": "name,title,description,type", "pageSize": 100 } }
api_read { "resource": "cubes", "method": "meta", "params": { "filter": "name == \"crm_deals\"" } }
The app lists measures of source cubes only. Leave out:
- — a table's view () re-exports measures under new
names; each such measure's points at the original;
- generated cubes, marked
meta.abConnectionId: "revos"
— segment
snapshots () and tables' own columns ().
can't be projected with ; it's on the full record you read
for a candidate cube anyway.
Per measure, what the catalogue shows:
| Column | From |
|---|
| Metric | ( as its tooltip), as the ref |
| Cube | the cube's |
| Agg | : , , , , , , , … |
| Format | / , else by type: Number, Text, Boolean, Date |
Only
measures can be counted on a card; say so if the user
picks a text, boolean, or time one.
When the user asks for a metric by meaning ("win rate", "ARR"), search titles
and descriptions. If nothing matches, say so and offer the closest, or adding
one with the cubes skill — never approximate silently with a different
measure.
Where a metric is used
- Tables: a view
model_<table id, '-' → '_'>
re-exports the measure
(aliasMember == "<measure>"
). Map the id back with
api_read { "resource": "tables", "params": { "fields": "id,name" } }
.
- Segments: the measure (or a view alias of it) is a in the
segment's .
- Dashboards:
api_read { "resource": "dashboard-cards", "params": { "filter": "measure == \"<measure>\"" } }
.
A metric's value
api_read { "resource": "cubes", "method": "query", "body": { "query": … } }
,
built exactly as the app builds it — otherwise your number differs from the
one on the user's screen:
json
{
"measures": ["<measure>"],
"filters": [ <the scope's conditions, as Cube filters> ],
"joinHints": [ <each join path, split on "."> ]
}
Scope
- Every row — no filters.
- Conditions — written in segment filter syntax (see the segments
skill's
references/filter-syntax.md
) and translated with its table:
→ , → , → , …
Top-level items are separate entries; an becomes
, an group . Skip conditions with no
, or no where the operator needs one — the app does too.
Check stored values with a cubes query before an on a string.
- A dynamic segment — read it; translate its the same way, and
put its into first.
- A static segment — one filter instead of its conditions:
{ "member": "segment_<segment id, '-' → '_'>.id", "operator": "set" }
.
It needs that cube in .
A segment fits a metric only when its
root cube is the metric's cube
(
segment.cubes[0].joinPath
). A segment of companies can't scope a deal
metric — say so, and offer conditions on the company fields instead.
Joins to other cubes
A condition or breakdown on another cube reads it along a
join path from
the metric's cube:
. Check each hop against
in
(declared on either side). When two routes exist, the choice
changes the number — ask which the user means. Send each path as a join
hint,
["crm_deals", "crm_companies"]
; the metric's own cube needs none.
Two conditions the app refuses, and you should too:
- A condition on a measure (). On a total it isn't
per-row; it's not a scope.
- A condition on a cube the metric's rows have many of ( /
along the path): each row is checked once per related row.
Breakdown
The same query plus the dimension:
- not a time dimension: ,
"order": { "<measure>": "desc", "<dim>": "asc" }
— largest groups first.
- a time dimension:
"timeDimensions": [{ "dimension": "<dim>", "granularity": "month" }]
,
"order": { "<dim>": "desc" }
— latest months first.
The groups add up to the total only for a
or
measure whose
breakdown doesn't go along a join the rows have many of. Otherwise don't
present shares or an "Other" bucket, and for a fanned-out breakdown, say a
record linked to several groups is counted in each. A card shows the top 5
plus "Other"; the metric page shows 50.
Reading the answer
- No rows: a count, count distinct, or sum is ; an average or other
measure has no value — say "no value", not .
- Format with the measure's (currency, percent).
- Always say what the number is counted over: "€1.2M open pipeline, deals
in the EMEA segment".
- on the result says how fresh it is; mention it
when it's old or is set.
Render a single value as one line, a breakdown as a small table, or a chart
when it helps.