The $raw Column
How Berserk resolves column names using $raw in permissive and strict mode
Berserk KQL resolves names differently than Microsoft's Kusto. In Microsoft Kusto, tables always have a fixed schema and unrecognized column names produce errors. Berserk introduces permissive mode and the special $raw column to support semi-structured data common in observability workloads.
What is $raw?
Every dataset (source table) in Berserk has exactly one column: $raw, a dynamic property bag containing the full original record. When data is ingested (e.g. from OpenTelemetry), the entire JSON object is stored in $raw. See OTEL Field Mapping for exactly how OpenTelemetry data is encoded as JSON.
Permissive vs Strict Mode
Berserk has two query modes that control how unknown field names are resolved:
Permissive Mode (default)
In permissive mode, bare identifiers that don't match a declared column are automatically resolved as property paths into $raw. This is called auto-projection.
default | where level == "INFO"If level is not a declared column but the table has $raw, this is equivalent to:
default | where $raw.level == "INFO"The auto-projected field has type dynamic, since its actual type is unknown at query time. The editor shows an informational hint on auto-projected fields to indicate they are being resolved from $raw.
Strict Mode
In strict mode, every column reference must match a declared column in the schema. Unknown fields produce an error:
-- Error: unknown column 'level'
Fruit | where level == "INFO"This is closer to Microsoft Kusto's behavior. Use strict mode when working with well-defined schemas where typos should be caught early.
Explicit $raw Access
You can always access $raw directly using dotted path syntax:
default | where $raw.level == "INFO"This works in both permissive and strict mode. Nested paths are supported:
default | where $raw.resource.attributes["service.name"] == "api-gateway"Array indexing is also supported:
default | where $raw.items[0].name == "first"Type Annotations
Since auto-projected fields are dynamic, arithmetic and comparison operations may need type hints. Use the annotate operator to declare expected types:
default
| annotate response_time:real, status_code:int
| where status_code >= 400
| summarize avg(response_time) by bin($time, 5m)Without annotation, response_time and status_code would be dynamic, and arithmetic like avg() would require explicit toreal() casts.
Comparison with Microsoft Kusto
| Behavior | Microsoft Kusto | Berserk (Permissive) | Berserk (Strict) |
|---|---|---|---|
| Unknown column | Error | Auto-project from $raw | Error |
$raw column | No equivalent | Built-in dynamic column | Built-in dynamic column |
| Type of auto-projected fields | N/A | dynamic | N/A |