Implementation:Langfuse Langfuse Event Query Builder
| Knowledge Sources | |
|---|---|
| Domains | ClickHouse, Query Building |
| Last Updated | 2026-02-14 00:00 GMT |
Overview
Provides a fluent, type-safe query builder hierarchy for constructing ClickHouse SQL queries against the events table, supporting field sets, aggregations, CTEs, JOINs, filtering, ordering, and pagination.
Description
This is the largest and most central query building module in Langfuse. It provides a class hierarchy for constructing all ClickHouse queries against the events table:
Field Definitions: The module defines EVENTS_FIELDS -- a complete mapping of field names to their SELECT expressions (e.g., id maps to e.span_id as id). It also defines FIELD_SETS -- predefined groupings of fields for common query patterns (base, calculated, io, metadata, tools, export, public API v2 groups, etc.). For trace-level aggregation, EVENTS_AGGREGATION_FIELDS provides field expressions using ClickHouse aggregation functions like argMaxIf, sumMap, groupUniqArrayIf, etc.
Class Hierarchy:
AbstractQueryBuilder: Base class providing WHERE, ORDER BY, LIMIT, LIMIT BY, conditional chaining (when), andbuildWithParams().AbstractCTEQueryBuilder: Extends the base with CTE (WITHclause) and LEFT JOIN support.BaseEventsQueryBuilder: Adds events-table-specific logic including automaticproject_idfiltering,orderByColumnswith automaticproject_idprepending for ClickHouse sort optimization, and the template method pattern for SELECT/GROUP BY.EventsQueryBuilder: The primary builder for observation-level queries. SupportsselectFieldSetfor predefined column groups, I/O field selection with truncation control, metadata expansion, direct metadata access for exports, and raw SELECT expressions.EventsAggregationQueryBuilder: For trace-level aggregation queries with GROUP BYtrace_id, using aggregation fields.EventsAggQueryBuilder: For observation-level aggregation queries (e.g., filter option lookups) with custom GROUP BY and SELECT expressions.
The NoProjectId symbol allows explicitly opting out of automatic project_id filtering for cross-project queries.
Usage
Use EventsQueryBuilder for standard observation queries (list, detail, export). Use EventsAggregationQueryBuilder for trace-level queries that aggregate across observations. Use EventsAggQueryBuilder for filter option lookups. Chain methods fluently and call buildWithParams() to get the final SQL and parameters.
Code Reference
Source Location
- Repository: Langfuse
- File: packages/shared/src/server/queries/clickhouse-sql/event-query-builder.ts
- Lines: 1-1167
Signature
export type OrderByDirection = "ASC" | "DESC";
export type OrderByEntry = { column: string; direction: OrderByDirection };
export type FieldSetName = keyof typeof FIELD_SETS;
export const NoProjectId: unique symbol;
export type NoProjectIdType = typeof NoProjectId;
// Abstract base classes (not exported but form the hierarchy)
// abstract class AbstractQueryBuilder
// abstract class AbstractCTEQueryBuilder extends AbstractQueryBuilder
// abstract class BaseEventsQueryBuilder<TFields> extends AbstractCTEQueryBuilder
export class EventsQueryBuilder extends BaseEventsQueryBuilder<typeof EVENTS_FIELDS> {
constructor(options: { projectId: string | NoProjectIdType });
selectFieldSet(...setNames: FieldSetName[]): this;
selectIO(include: boolean, charLimit?: number): this;
selectMetadataExpanded(keys: string[]): this;
selectMetadataDirect(): this;
selectRaw(...expressions: string[]): this;
// Inherited: whereRaw, where, orderBy, orderByColumns, orderByDefault,
// limit, limitBy, when, withCTE, leftJoin, buildWithParams
}
export class EventsAggregationQueryBuilder extends BaseEventsQueryBuilder<typeof EVENTS_AGGREGATION_FIELDS> {
constructor(options: { projectId: string | NoProjectIdType });
selectFieldSet(...setNames: Array<keyof typeof AGGREGATION_FIELD_SETS>): this;
// Inherited: same chainable methods
}
export class EventsAggQueryBuilder extends AbstractCTEQueryBuilder {
constructor(options: {
projectId: string;
groupByColumn: string;
selectExpression: string;
});
// Inherited: whereRaw, where, orderBy, limit, withCTE, leftJoin, buildWithParams
}
Import
import {
EventsQueryBuilder,
EventsAggregationQueryBuilder,
EventsAggQueryBuilder,
NoProjectId,
type FieldSetName,
type OrderByEntry,
} from "@langfuse/shared/src/server/queries/clickhouse-sql/event-query-builder";
I/O Contract
Inputs
| Name | Type | Required | Description |
|---|---|---|---|
| projectId | string or NoProjectIdType | Yes | Project ID for automatic WHERE filtering, or NoProjectId to skip |
| setNames | FieldSetName[] | No | Predefined field set names to include in SELECT (e.g., "base", "calculated", "io") |
| condition | string | No | Raw WHERE clause condition with ClickHouse parameter syntax |
| params | Record<string, any> | No | Parameter values for parameterized conditions |
| entries | OrderByEntry[] | No | Structured ORDER BY entries with column and direction |
| limit / offset | number | No | Pagination parameters |
Outputs
| Name | Type | Description |
|---|---|---|
| query | string | Complete ClickHouse SQL query string with parameterized placeholders |
| params | Record<string, any> | All accumulated parameter values for query execution |
Usage Examples
import { EventsQueryBuilder } from "@langfuse/shared/src/server/queries/clickhouse-sql/event-query-builder";
// Build an observation list query
const builder = new EventsQueryBuilder({ projectId: "my-project-id" })
.selectFieldSet("base", "calculated")
.selectIO(true, 1000)
.whereRaw("e.type = {type: String}", { type: "GENERATION" })
.orderByColumns([
{ column: "e.start_time", direction: "DESC" },
{ column: "e.event_ts", direction: "DESC" },
])
.limit(50, 0);
const { query, params } = builder.buildWithParams();
// Execute with ClickHouse client: clickhouse.query({ query, params })
// Aggregation query for traces
import { EventsAggregationQueryBuilder } from "...";
const aggBuilder = new EventsAggregationQueryBuilder({ projectId: "my-project-id" })
.selectFieldSet("all")
.whereRaw("e.start_time >= {from: DateTime64(3)}", { from: Date.now() - 86400000 })
.orderByDefault()
.limit(100, 0);
const { query: aggQuery, params: aggParams } = aggBuilder.buildWithParams();