Berserk Docs
Aggregate FunctionsDynamic / Graph

make_graph_by

Builds a **collapsed** `{nodes, edges}` graph directly from parent-linked rows: one node per distinct `key` value, and an edge for each row whose immediate parent carries a different `key`.

A node's count covers every row merged into it; an edge's count covers the parent-child pairs that cross that boundary, one per hand-off. A span graph collapsed by service name is a service dependency map; by process name, a process graph.

After id, parent_id, time, and key, pass inline "<agg>:<attr>", value pairs. Each names an output attribute and the per-row expression to aggregate into it, with agg one of sum, avg, min, max, countif, set, any, first, or p<N> for a percentile (p95, p99.9; the same sketch as percentile()). An edge aggregates only the child row of each crossing. The intrinsic count is summed and time min-merged; a null key collapses into "unknown"; null values are skipped rather than folded. An id that appears on more than one row raises an AggregateDegraded warning quoting a few such ids, which points at the instrumentation or at a double ingest.

Sampling whole traces. The aggregate keeps a small stub for every row it has seen, so over a wide time range it can outgrow its memory budget. Pass "sample_by", trace_id among the pairs and it then keeps whole traces instead of failing: 1 in 4, 16, … up to 1024 of them, chosen by trace id, so every kept child still has its parent and the same traces are kept everywhere. A sampled graph scales count, countif and sum back up by the sampling factor and carries sample_rate, the fraction kept; it raises no warning, since sampling is what the query asked for. avg and percentiles are computed from the kept traces as they are; min, max, set, any and first cover only the kept traces, and an edge seen in few traces may be missing. While the state fits, the result is exactly the unsampled one. A row with no trace id is left out, and an AggregateDegraded warning counts such rows.

The result is identical to summarize_graph(make_graph(id, parent_id, time, "<attr>", value, …), key, "<agg>:<attr>", …), but the collapse happens as rows arrive, so the aggregate retains a small stub per row instead of the row itself. Prefer it whenever the collapsed graph is what you want — the two-step form only pays off when you also need the raw per-id graph, or want to collapse the same graph by several different keys. (bzrk extension)

Syntax

make_graph_by(id, parent_id, time, key)

Parameters

Prop

Type

Returns: dynamic

Syntax

make_graph_by(id, parent_id, time, key, agg)

Parameters

Prop

Type

Returns: dynamic

Examples

Example 1

spans
| summarize map = make_graph_by(
  span_id,
  parent_span_id,
  start_time,
  tostring(resource["service.name"]),
  "countif:errors",
  status_code == "ERROR",
  "max:duration",
  duration
)
map (dynamic)
{"edges":[],"nodes":[{"attrs":{"duration":00:00:00.2000000,"errors":0},"count":9,"id":"","time":1704103200000000000}]}

Example 2

spans
| summarize map = make_graph_by(
  span_id,
  parent_span_id,
  start_time,
  tostring(resource["service.name"]),
  "sample_by",
  trace_id,
  "countif:errors",
  status_code == "ERROR"
)
map (dynamic)
{"edges":[],"nodes":[{"attrs":{"errors":0},"count":9,"id":"","time":1704103200000000000}]}

On this page