summarize_graph
Collapse a canonical `{nodes, edges}` graph (from `make_graph`) by a node attribute.
Nodes sharing the same key value merge, and an edge is kept for
every node whose immediate parent carries a different key — collapsing a
span graph by service yields a service dependency graph, by exe a
process-name graph.
A node's count covers every node merged into it; an edge's count covers
the parent-child pairs that cross that boundary, one per hand-off. A service
emitting ten spans per request therefore carries ten on its node and one on
its inbound edge for each request.
Each trailing "<agg>:<attr>" argument aggregates that attribute across the
merged nodes, and on an edge across the child of each crossing only, so a
callee's internal spans reach its node but not its inbound edge: sum, avg
(numeric), min, max (numeric/datetime, or strings compared
lexicographically — the comparison follows the type of the earliest value by
time, so a numbers-then-strings mix is undefined), countif (boolean →
count of trues), set (distinct values), any (a representative), first
(value at the earliest time), p<N> (percentile N of the numeric values,
e.g. p95 or p99.9, from the same sketch as percentile(); like avg, it
ranks the values it is given, so over already-collapsed graphs those are
per-graph results). The intrinsic count is summed and time min-merged
automatically; the key becomes the new node id; unlisted attributes are
dropped. Returns null if g is not a graph or key is null.
make_graph_by fuses this collapse into the make_graph aggregate and
returns the same graph from one pass; prefer it unless you also need the raw
per-id graph, or want to collapse the same graph by several different keys.
Syntax
summarize_graph(g, key, aggs)Parameters
Prop
Type
Given a dynamic field
This parameter is string-only. In permissive mode, which every query runs in, almost every field arrives as a dynamic, so passing one works because Berserk injects asstring: extract-or-null, the value when it really is a string and null otherwise, so the call yields null (a predicate yields false). A property bag or array is therefore not matched, and tostring() is never applied implicitly — use it explicitly to match a bag's JSON text, keys included. To search the strings inside an object or array, write body.* contains "x" (also has, hasprefix and hassuffix). See String coercion and the asXXX family.
Returns: dynamic
Examples
Example 1
spans
| summarize g = make_graph(
span_id,
parent_span_id,
start_time,
"service",
resource['service.name'],
"errors",
status_code == "ERROR"
)
by trace_id
| project g = summarize_graph(g, "service", "countif:errors")| g (dynamic) |
|---|
| {"edges":[],"nodes":[{"attrs":{"errors":0},"count":2,"id":"unknown","time":1704103260000000000}]} |
| {"edges":[],"nodes":[{"attrs":{"errors":0},"count":2,"id":"unknown","time":1704103320000000000}]} |
| {"edges":[],"nodes":[{"attrs":{"errors":0},"count":5,"id":"unknown","time":1704103200000000000}]} |
similar_bands
Embeds `text` with the semantic-search model, takes the sign of each coordinate of the embedding after a fixed random rotation (a SimHash), and returns one key per band of `rows_per_band` sign bits, as a dynamic array of `bands` longs, for LSH bucketing with `mv-expand` and `summarize by`.
treepath
Enumerate the JSON paths (in bracket notation) reachable inside a dynamic value.