> ## Documentation Index
> Fetch the complete documentation index at: https://docs.litejoin.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Windows

> Aggregate streaming data over time intervals with tumbling, sliding, and session windows.

# Time Windows & Aggregations

Windows group messages by time intervals for aggregation. Instead of reacting to every individual message, windows let you ask questions like "How many orders in the last 5 minutes?" or "What's the average temperature over a rolling 10-minute window?"

## Window Types

LiteJoin supports three window types:

### Tumbling Windows

Fixed-size, non-overlapping intervals. Each message belongs to **exactly one** window. When the window closes, the aggregation fires and results are emitted.

```yaml theme={null}
windows:
  - name: order-count
    type: tumbling
    size: 5m
    topic: orders
    query: |
      SELECT
        COUNT(*) as order_count,
        SUM(json_extract(payload, '$.amount')) as total_revenue
      FROM orders
    sink: dashboard
```

```
Time:  |--5m--|--5m--|--5m--|
       [  W1  ][  W2  ][  W3  ]
```

**Best for:** periodic reports, batch-style aggregations, non-overlapping summaries.

### Sliding Windows

Fixed-size intervals that **slide forward** by a configurable step. Messages may appear in **multiple windows**.

```yaml theme={null}
windows:
  - name: avg-order
    type: sliding
    size: 10m
    slide: 1m
    topic: orders
    query: |
      SELECT
        AVG(json_extract(payload, '$.amount')) as avg_amount,
        COUNT(*) as count
      FROM orders
    sink: dashboard
```

```
Time:  |----10m----|
         |----10m----|
           |----10m----|
       <-1m->
```

**Best for:** moving averages, trend detection, smoothed metrics.

### Session Windows

Dynamic intervals based on **activity gaps**. A session starts with the first message and closes when no new messages arrive within the `gap` duration.

```yaml theme={null}
windows:
  - name: user-session
    type: session
    gap: 30m
    topic: user_events
    query: |
      SELECT
        COUNT(*) as event_count,
        MIN(timestamp) as session_start,
        MAX(timestamp) as session_end
      FROM user_events
    sink: analytics
```

```
Time:  |msg|  |msg|msg|     |--- gap > 30m ---|  |msg|msg|
       [    session 1    ]                        [ session 2 ]
```

**Best for:** user sessions, burst detection, grouping related events.

## Configuration

### Common Fields

| Field   | Type   | Required | Description                          |
| ------- | ------ | -------- | ------------------------------------ |
| `name`  | string | yes      | Unique name for the window.          |
| `type`  | string | yes      | `tumbling`, `sliding`, or `session`. |
| `topic` | string | yes      | Topic to aggregate.                  |
| `query` | string | yes      | SQL aggregation query.               |
| `sink`  | string | yes      | Sink to emit results to.             |

### Type-Specific Fields

| Field   | Applies To        | Type     | Description                        |
| ------- | ----------------- | -------- | ---------------------------------- |
| `size`  | tumbling, sliding | duration | Window size (e.g., `5m`, `1h`).    |
| `slide` | sliding           | duration | Slide interval. Must be ≤ `size`.  |
| `gap`   | session           | duration | Inactivity gap to close a session. |

## Result Format

Window results are emitted as `JoinResult` objects, the same format as join results:

```json theme={null}
{
  "query": "order-count",
  "key": "window_1740268800_1740269100",
  "rows": [
    {
      "order_count": 42,
      "total_revenue": 3850.00
    }
  ],
  "emit_at": "2026-02-22T10:05:00Z"
}
```

## Combining Windows with Joins

You can use window aggregations as inputs to joins. For example, aggregate orders per region, then join with region metadata:

```yaml theme={null}
windows:
  - name: orders-by-region
    type: tumbling
    size: 5m
    topic: orders
    query: |
      SELECT
        json_extract(payload, '$.region') as region,
        COUNT(*) as count,
        SUM(json_extract(payload, '$.amount')) as revenue
      FROM orders
      GROUP BY json_extract(payload, '$.region')
    sink: dashboard
```

## Best Practices

1. **Choose the right window type.** Tumbling for periodic reports, sliding for moving averages, session for activity-based grouping.
2. **Keep `size` reasonable.** Larger windows hold more data in memory. Start with 5–15 minute windows.
3. **Use `GROUP BY` for segmentation.** Split aggregations by a dimension (region, user type, status) for richer insights.
4. **Test with Studio.** Use the visual windowing controls in [LiteJoin Studio](/getting-started/studio) to experiment without writing complex config.
