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

# Worked examples

> Three importable topologies — a stateless pipeline, the classic word count, and a windowed count — to open, study, and run.

Each example below is a complete design you can import: save the JSON as a
file, open **Streams → Create Stream**, click **Upload** in the wizard
header, and pick the file. The canvas and configuration load ready to
explore — or to submit.

To actually run one:

1. Create its input topic on the **Topics** page (the topic names are in each
   example).
2. Check **Stream Properties** — `bootstrap.servers` should point at your
   cluster.
3. **Submit**, then produce a few messages to the input topic (Topics →
   *Create Message*) and watch the output topic.

## 1 — Filter and transform (stateless)

`demo-input` → **Filter** → **Map Values** → `demo-output`. Keeps only
records whose value contains `error`, then upper-cases them. Two SpEL
expressions, no state — the smallest useful pipeline:

```text theme={null}
value.contains("error")     // Filter
value.toUpperCase()         // Map Values
```

<Accordion title="filterAndMapValues.json">
  ```json theme={null}
  {
    "configuration": {
      "application.id": "filterAndMapValues"
    },
    "nodes": [
      {
        "id": "source1",
        "type": "SOURCE_STREAM",
        "position": { "x": 300, "y": 0 },
        "data": {
          "streamSourceTarget": "STREAM",
          "inputTopics": ["demo-input"],
          "keySerde": "String",
          "valueSerde": "String"
        }
      },
      {
        "id": "filter1",
        "type": "FILTER",
        "position": { "x": 300, "y": 150 },
        "data": {
          "expression": {
            "expression": "value.contains(\"error\")",
            "language": "SpEL"
          }
        }
      },
      {
        "id": "mapValues1",
        "type": "MAP_VALUES",
        "position": { "x": 300, "y": 300 },
        "data": {
          "expression": {
            "expression": "value.toUpperCase()",
            "language": "SpEL"
          }
        }
      },
      {
        "id": "sink1",
        "type": "SINK_NODE",
        "position": { "x": 300, "y": 450 },
        "data": {
          "topicName": "demo-output",
          "keySerde": "String",
          "valueSerde": "String"
        }
      }
    ],
    "edges": [
      {
        "animated": false,
        "source": "source1",
        "target": "filter1",
        "type": "deletable-edge",
        "markerEnd": { "type": "arrow", "width": 30, "height": 30, "color": "#b1b1b7" },
        "data": { "label": null },
        "id": "xy-edge__source1-filter1"
      },
      {
        "animated": false,
        "source": "filter1",
        "target": "mapValues1",
        "type": "deletable-edge",
        "markerEnd": { "type": "arrow", "width": 30, "height": 30, "color": "#b1b1b7" },
        "data": { "label": null },
        "id": "xy-edge__filter1-mapValues1"
      },
      {
        "animated": false,
        "source": "mapValues1",
        "target": "sink1",
        "type": "deletable-edge",
        "markerEnd": { "type": "arrow", "width": 30, "height": 30, "color": "#b1b1b7" },
        "data": { "label": null },
        "id": "xy-edge__mapValues1-sink1"
      }
    ]
  }
  ```
</Accordion>

## 2 — Word count (stateful)

The classic. `text-lines` → **FlatMap Values** (tokenize) → **Group By**
(the word becomes the key) → **Count** → **To Stream** → `word-counts`.

```text theme={null}
value.toLowerCase().split('\\W+')   // FlatMap Values: one record per word
value                               // Group By: group by the word itself
```

**Count** produces a *KTable* of running totals, so its value type is `Long` —
note the sink's value serde. **To Stream** converts the table's changelog back
into a stream for the sink. Produce a few text lines and watch per-word counts
accumulate on `word-counts`.

<Accordion title="wordCount.json">
  ```json theme={null}
  {
    "configuration": {
      "application.id": "wordCount"
    },
    "nodes": [
      {
        "id": "src",
        "type": "SOURCE_STREAM",
        "position": { "x": 300, "y": 0 },
        "data": {
          "streamSourceTarget": "STREAM",
          "inputTopics": ["text-lines"],
          "keySerde": "String",
          "valueSerde": "String"
        }
      },
      {
        "id": "flatMap",
        "type": "FLATMAP_VALUES",
        "position": { "x": 300, "y": 150 },
        "data": {
          "expression": {
            "expression": "value.toLowerCase().split('\\W+')",
            "language": "SpEL"
          }
        }
      },
      {
        "id": "groupBy",
        "type": "GROUP_BY",
        "position": { "x": 300, "y": 300 },
        "data": {
          "expression": {
            "expression": "value",
            "language": "SpEL"
          },
          "keySerde": "String",
          "valueSerde": "String"
        }
      },
      {
        "id": "count",
        "type": "COUNT",
        "position": { "x": 300, "y": 450 },
        "data": {}
      },
      {
        "id": "toStream",
        "type": "TO_STREAM",
        "position": { "x": 300, "y": 600 },
        "data": {}
      },
      {
        "id": "sink",
        "type": "SINK_NODE",
        "position": { "x": 300, "y": 750 },
        "data": {
          "topicName": "word-counts",
          "keySerde": "String",
          "valueSerde": "Long"
        }
      }
    ],
    "edges": [
      {
        "id": "e1",
        "animated": false,
        "source": "src",
        "target": "flatMap",
        "type": "deletable-edge",
        "markerEnd": { "type": "arrow", "width": 30, "height": 30, "color": "#b1b1b7" },
        "data": { "label": null }
      },
      {
        "id": "e2",
        "animated": false,
        "source": "flatMap",
        "target": "groupBy",
        "type": "deletable-edge",
        "markerEnd": { "type": "arrow", "width": 30, "height": 30, "color": "#b1b1b7" },
        "data": { "label": null }
      },
      {
        "id": "e3",
        "animated": false,
        "source": "groupBy",
        "target": "count",
        "type": "deletable-edge",
        "markerEnd": { "type": "arrow", "width": 30, "height": 30, "color": "#b1b1b7" },
        "data": { "label": null }
      },
      {
        "id": "e4",
        "animated": false,
        "source": "count",
        "target": "toStream",
        "type": "deletable-edge",
        "markerEnd": { "type": "arrow", "width": 30, "height": 30, "color": "#b1b1b7" },
        "data": { "label": null }
      },
      {
        "id": "e5",
        "animated": false,
        "source": "toStream",
        "target": "sink",
        "type": "deletable-edge",
        "markerEnd": { "type": "arrow", "width": 30, "height": 30, "color": "#b1b1b7" },
        "data": { "label": null }
      }
    ]
  }
  ```
</Accordion>

## 3 — Tumbling-window count (windowed)

`windowing-input` → **Group By Key** → **Tumbling Window** (5 seconds) →
**Count** → **To Stream** → `window-out-tumbling`. Counts records *per key,
per 5-second window* — the same count pattern as word count, but the total
resets with each window instead of running forever. No expressions at all:
grouping is by the existing key, and the window node just carries its size
(`windowSizeMs: 5000`).

Produce several messages with the same key in quick succession, wait past the
window, produce again — the output shows a count per window.

<Accordion title="tumblingWindow.json">
  ```json theme={null}
  {
    "configuration": {
      "application.id": "tumblingWindow"
    },
    "nodes": [
      {
        "id": "src",
        "type": "SOURCE_STREAM",
        "position": { "x": 300, "y": 0 },
        "data": {
          "streamSourceTarget": "STREAM",
          "inputTopics": ["windowing-input"],
          "keySerde": "String",
          "valueSerde": "String"
        }
      },
      {
        "id": "groupByKey",
        "type": "GROUP_BY_KEY",
        "position": { "x": 300, "y": 150 },
        "data": {}
      },
      {
        "id": "window",
        "type": "TUMBLING_WINDOW",
        "position": { "x": 300, "y": 300 },
        "data": {
          "windowSizeMs": 5000
        }
      },
      {
        "id": "count",
        "type": "COUNT",
        "position": { "x": 300, "y": 450 },
        "data": {}
      },
      {
        "id": "toStream",
        "type": "TO_STREAM",
        "position": { "x": 300, "y": 600 },
        "data": {}
      },
      {
        "id": "sink",
        "type": "SINK_NODE",
        "position": { "x": 300, "y": 750 },
        "data": {
          "topicName": "window-out-tumbling",
          "keySerde": "String",
          "valueSerde": "Long"
        }
      }
    ],
    "edges": [
      {
        "id": "e1",
        "animated": false,
        "source": "src",
        "target": "groupByKey",
        "type": "deletable-edge",
        "markerEnd": { "type": "arrow", "width": 30, "height": 30, "color": "#b1b1b7" },
        "data": { "label": null }
      },
      {
        "id": "e2",
        "animated": false,
        "source": "groupByKey",
        "target": "window",
        "type": "deletable-edge",
        "markerEnd": { "type": "arrow", "width": 30, "height": 30, "color": "#b1b1b7" },
        "data": { "label": null }
      },
      {
        "id": "e3",
        "animated": false,
        "source": "window",
        "target": "count",
        "type": "deletable-edge",
        "markerEnd": { "type": "arrow", "width": 30, "height": 30, "color": "#b1b1b7" },
        "data": { "label": null }
      },
      {
        "id": "e4",
        "animated": false,
        "source": "count",
        "target": "toStream",
        "type": "deletable-edge",
        "markerEnd": { "type": "arrow", "width": 30, "height": 30, "color": "#b1b1b7" },
        "data": { "label": null }
      },
      {
        "id": "e5",
        "animated": false,
        "source": "toStream",
        "target": "sink",
        "type": "deletable-edge",
        "markerEnd": { "type": "arrow", "width": 30, "height": 30, "color": "#b1b1b7" },
        "data": { "label": null }
      }
    ]
  }
  ```
</Accordion>

## Where to go from here

Duplicate a node, change an expression, re-validate — the
[building guide](/streams/building) covers the canvas, and the
[SpEL reference](/streams/spel) covers everything you can write in an
expression. And remember to **Download** your variants:
[designs live in the file](/streams/deploy#restarts-and-the-edition-behaviour),
not the server.
