[{"data":1,"prerenderedAt":4471},["ShallowReactive",2],{"navigation":3,"\u002Fdocs\u002Fbuilding\u002Faggregations":314,"\u002Fdocs\u002Fbuilding\u002Faggregations-surround":4466},[4,38,86,149,176,219,257,273],{"title":5,"path":6,"stem":7,"children":8,"icon":37},"Getting Started","\u002Fdocs\u002Fgetting-started","1.docs\u002F1.getting-started\u002F1.index",[9,12,17,22,27,32],{"title":10,"path":6,"stem":7,"icon":11},"Getting started","i-lucide-flag",{"title":13,"path":14,"stem":15,"icon":16},"Modules overview","\u002Fdocs\u002Fgetting-started\u002Fmodules-overview","1.docs\u002F1.getting-started\u002F2.modules-overview","i-lucide-boxes",{"title":18,"path":19,"stem":20,"icon":21},"Installation","\u002Fdocs\u002Fgetting-started\u002Finstallation","1.docs\u002F1.getting-started\u002F3.installation","i-lucide-download",{"title":23,"path":24,"stem":25,"icon":26},"License configuration","\u002Fdocs\u002Fgetting-started\u002Flicense-configuration","1.docs\u002F1.getting-started\u002F4.license-configuration","i-lucide-key-round",{"title":28,"path":29,"stem":30,"icon":31},"Your first app","\u002Fdocs\u002Fgetting-started\u002Ffirst-app","1.docs\u002F1.getting-started\u002F5.first-app","i-lucide-square-play",{"title":33,"path":34,"stem":35,"icon":36},"Project setup","\u002Fdocs\u002Fgetting-started\u002Fproject-setup","1.docs\u002F1.getting-started\u002F6.project-setup","i-lucide-package",false,{"title":39,"path":40,"stem":41,"children":42,"icon":45},"Core Concepts","\u002Fdocs\u002Fconcepts","1.docs\u002F2.concepts\u002F1.index",[43,46,51,56,61,66,71,76,81],{"title":44,"path":40,"stem":41,"icon":45},"Core concepts","i-lucide-book-open",{"title":47,"path":48,"stem":49,"icon":50},"Architecture","\u002Fdocs\u002Fconcepts\u002Farchitecture","1.docs\u002F2.concepts\u002F2.architecture","i-lucide-layers",{"title":52,"path":53,"stem":54,"icon":55},"Exactly-once semantics","\u002Fdocs\u002Fconcepts\u002Fexactly-once","1.docs\u002F2.concepts\u002F3.exactly-once","i-lucide-shield-check",{"title":57,"path":58,"stem":59,"icon":60},"Lanes and parallelism","\u002Fdocs\u002Fconcepts\u002Flanes-and-parallelism","1.docs\u002F2.concepts\u002F4.lanes-and-parallelism","i-lucide-split",{"title":62,"path":63,"stem":64,"icon":65},"State and thread-safety","\u002Fdocs\u002Fconcepts\u002Fstate-and-thread-safety","1.docs\u002F2.concepts\u002F5.state-and-thread-safety","i-lucide-lock",{"title":67,"path":68,"stem":69,"icon":70},"Event time and watermarks","\u002Fdocs\u002Fconcepts\u002Fevent-time-and-watermarks","1.docs\u002F2.concepts\u002F6.event-time-and-watermarks","i-lucide-clock",{"title":72,"path":73,"stem":74,"icon":75},"The configuration model","\u002Fdocs\u002Fconcepts\u002Fconfiguration-model","1.docs\u002F2.concepts\u002F7.configuration-model","i-lucide-sliders-horizontal",{"title":77,"path":78,"stem":79,"icon":80},"The error-handling model","\u002Fdocs\u002Fconcepts\u002Ferror-handling-model","1.docs\u002F2.concepts\u002F8.error-handling-model","i-lucide-triangle-alert",{"title":82,"path":83,"stem":84,"icon":85},"How StoatFlow differs from Kafka Streams","\u002Fdocs\u002Fconcepts\u002Fhow-stoatflow-differs-from-ks","1.docs\u002F2.concepts\u002F9.how-stoatflow-differs-from-ks","i-lucide-arrow-left-right",{"title":87,"path":88,"stem":89,"children":90,"icon":93},"Building Topologies","\u002Fdocs\u002Fbuilding","1.docs\u002F3.building\u002F1.index",[91,94,99,104,109,114,119,124,129,134,139,144],{"title":92,"path":88,"stem":89,"icon":93},"Building topologies","i-lucide-blocks",{"title":95,"path":96,"stem":97,"icon":98},"Error handling and DLQ","\u002Fdocs\u002Fbuilding\u002Ferror-handling-dlq","1.docs\u002F3.building\u002F10.error-handling-dlq","i-lucide-circle-x",{"title":100,"path":101,"stem":102,"icon":103},"State stores","\u002Fdocs\u002Fbuilding\u002Fstate-stores","1.docs\u002F3.building\u002F11.state-stores","i-lucide-database",{"title":105,"path":106,"stem":107,"icon":108},"Testing topologies","\u002Fdocs\u002Fbuilding\u002Ftesting","1.docs\u002F3.building\u002F12.testing","i-lucide-flask-conical",{"title":110,"path":111,"stem":112,"icon":113},"Sources and sinks","\u002Fdocs\u002Fbuilding\u002Fstreams-builder","1.docs\u002F3.building\u002F2.streams-builder","i-lucide-import",{"title":115,"path":116,"stem":117,"icon":118},"KStream and KTable operations","\u002Fdocs\u002Fbuilding\u002Fkstream-ktable","1.docs\u002F3.building\u002F3.kstream-ktable","i-lucide-waypoints",{"title":120,"path":121,"stem":122,"icon":123},"Aggregations","\u002Fdocs\u002Fbuilding\u002Faggregations","1.docs\u002F3.building\u002F4.aggregations","i-lucide-sigma",{"title":125,"path":126,"stem":127,"icon":128},"Windowing","\u002Fdocs\u002Fbuilding\u002Fwindowing","1.docs\u002F3.building\u002F5.windowing","i-lucide-calendar-clock",{"title":130,"path":131,"stem":132,"icon":133},"Joins","\u002Fdocs\u002Fbuilding\u002Fjoins","1.docs\u002F3.building\u002F6.joins","i-lucide-git-merge",{"title":135,"path":136,"stem":137,"icon":138},"The Processor API","\u002Fdocs\u002Fbuilding\u002Fprocessor-api","1.docs\u002F3.building\u002F7.processor-api","i-lucide-cpu",{"title":140,"path":141,"stem":142,"icon":143},"Scheduled sources","\u002Fdocs\u002Fbuilding\u002Fscheduled-sources","1.docs\u002F3.building\u002F8.scheduled-sources","i-lucide-timer",{"title":145,"path":146,"stem":147,"icon":148},"Serdes and Avro","\u002Fdocs\u002Fbuilding\u002Fserdes","1.docs\u002F3.building\u002F9.serdes","i-lucide-binary",{"title":150,"path":151,"stem":152,"children":153,"icon":75},"Configuration","\u002Fdocs\u002Fconfiguration","1.docs\u002F4.configuration\u002F1.index",[154,156,161,166,171],{"title":155,"path":151,"stem":152,"icon":75},"How configuration works",{"title":157,"path":158,"stem":159,"icon":160},"Engine configuration (:core)","\u002Fdocs\u002Fconfiguration\u002Fcore-config","1.docs\u002F4.configuration\u002F2.core-config","i-lucide-settings-2",{"title":162,"path":163,"stem":164,"icon":165},"Runtime configuration (:runtime)","\u002Fdocs\u002Fconfiguration\u002Fruntime-config","1.docs\u002F4.configuration\u002F3.runtime-config","i-lucide-server-cog",{"title":167,"path":168,"stem":169,"icon":170},"Defaults, adaptivity, and presets","\u002Fdocs\u002Fconfiguration\u002Fdefaults-and-presets","1.docs\u002F4.configuration\u002F4.defaults-and-presets","i-lucide-gauge",{"title":172,"path":173,"stem":174,"icon":175},"Kafka client configuration","\u002Fdocs\u002Fconfiguration\u002Fkafka-client-config","1.docs\u002F4.configuration\u002F5.kafka-client-config","i-lucide-plug",{"title":177,"path":178,"stem":179,"children":180,"icon":183},"Running in Production","\u002Fdocs\u002Fruntime","1.docs\u002F5.runtime\u002F1.index",[181,184,189,194,199,204,209,214],{"title":182,"path":178,"stem":179,"icon":183},"The runtime","i-lucide-server",{"title":185,"path":186,"stem":187,"icon":188},"The REST API","\u002Fdocs\u002Fruntime\u002Frest-api","1.docs\u002F5.runtime\u002F2.rest-api","i-lucide-globe",{"title":190,"path":191,"stem":192,"icon":193},"Health checks","\u002Fdocs\u002Fruntime\u002Fhealth-checks","1.docs\u002F5.runtime\u002F3.health-checks","i-lucide-heart-pulse",{"title":195,"path":196,"stem":197,"icon":198},"Metrics","\u002Fdocs\u002Fruntime\u002Fmetrics","1.docs\u002F5.runtime\u002F4.metrics","i-lucide-activity",{"title":200,"path":201,"stem":202,"icon":203},"Pause and resume","\u002Fdocs\u002Fruntime\u002Fpause-unpause","1.docs\u002F5.runtime\u002F5.pause-unpause","i-lucide-pause",{"title":205,"path":206,"stem":207,"icon":208},"Plugins and lifecycle hooks","\u002Fdocs\u002Fruntime\u002Fplugins","1.docs\u002F5.runtime\u002F6.plugins","i-lucide-puzzle",{"title":210,"path":211,"stem":212,"icon":213},"Docker images","\u002Fdocs\u002Fruntime\u002Fdocker","1.docs\u002F5.runtime\u002F7.docker","i-lucide-container",{"title":215,"path":216,"stem":217,"icon":218},"GraalVM native image","\u002Fdocs\u002Fruntime\u002Fnative-image","1.docs\u002F5.runtime\u002F8.native-image","i-lucide-zap",{"title":220,"path":221,"stem":222,"children":223,"icon":226},"Deploying & Operating","\u002Fdocs\u002Foperating","1.docs\u002F6.operating\u002F1.index",[224,227,232,237,242,247,252],{"title":225,"path":221,"stem":222,"icon":226},"Deploying and operating","i-lucide-life-buoy",{"title":228,"path":229,"stem":230,"icon":231},"Running on Kubernetes","\u002Fdocs\u002Foperating\u002Fkubernetes","1.docs\u002F6.operating\u002F2.kubernetes","i-lucide-ship",{"title":233,"path":234,"stem":235,"icon":236},"High availability","\u002Fdocs\u002Foperating\u002Fhigh-availability","1.docs\u002F6.operating\u002F3.high-availability","i-lucide-copy",{"title":238,"path":239,"stem":240,"icon":241},"Liveness and readiness probes","\u002Fdocs\u002Foperating\u002Fprobes","1.docs\u002F6.operating\u002F4.probes","i-lucide-stethoscope",{"title":243,"path":244,"stem":245,"icon":246},"Observability","\u002Fdocs\u002Foperating\u002Fobservability","1.docs\u002F6.operating\u002F5.observability","i-lucide-telescope",{"title":248,"path":249,"stem":250,"icon":251},"Tuning under load","\u002Fdocs\u002Foperating\u002Ftuning","1.docs\u002F6.operating\u002F6.tuning","i-lucide-sliders",{"title":253,"path":254,"stem":255,"icon":256},"Production checklist","\u002Fdocs\u002Foperating\u002Fproduction-checklist","1.docs\u002F6.operating\u002F7.production-checklist","i-lucide-clipboard-check",{"title":258,"path":259,"stem":260,"children":261,"icon":85},"Migrating from Kafka Streams","\u002Fdocs\u002Fmigration","1.docs\u002F7.migration\u002F1.index",[262,263,268],{"title":258,"path":259,"stem":260,"icon":85},{"title":264,"path":265,"stem":266,"icon":267},"Migration without carrying state","\u002Fdocs\u002Fmigration\u002Fwithout-data-migration","1.docs\u002F7.migration\u002F2.without-data-migration","i-lucide-sparkles",{"title":269,"path":270,"stem":271,"icon":272},"Migration carrying state","\u002Fdocs\u002Fmigration\u002Fwith-data-migration","1.docs\u002F7.migration\u002F3.with-data-migration","i-lucide-database-backup",{"title":274,"path":275,"stem":276,"children":277,"icon":279},"Reference","\u002Fdocs\u002Freference","1.docs\u002F8.reference\u002F1.index",[278,280,285,290,295,300,305,309],{"title":274,"path":275,"stem":276,"icon":279},"i-lucide-list",{"title":281,"path":282,"stem":283,"icon":284},"Configuration reference","\u002Fdocs\u002Freference\u002Fconfiguration-reference","1.docs\u002F8.reference\u002F2.configuration-reference","i-lucide-table",{"title":286,"path":287,"stem":288,"icon":289},"REST API reference","\u002Fdocs\u002Freference\u002Frest-api-reference","1.docs\u002F8.reference\u002F3.rest-api-reference","i-lucide-network",{"title":291,"path":292,"stem":293,"icon":294},"Gradle plugin reference","\u002Fdocs\u002Freference\u002Fgradle-plugin-reference","1.docs\u002F8.reference\u002F4.gradle-plugin-reference","i-lucide-box",{"title":296,"path":297,"stem":298,"icon":299},"Maven reference","\u002Fdocs\u002Freference\u002Fmaven-reference","1.docs\u002F8.reference\u002F5.maven-reference","i-simple-icons-apachemaven",{"title":301,"path":302,"stem":303,"icon":304},"Kafka Streams compatibility matrix","\u002Fdocs\u002Freference\u002Fks-compatibility-matrix","1.docs\u002F8.reference\u002F6.ks-compatibility-matrix","i-lucide-table-2",{"title":306,"path":307,"stem":308,"icon":170},"Metrics reference","\u002Fdocs\u002Freference\u002Fmetrics-reference","1.docs\u002F8.reference\u002F7.metrics-reference",{"title":310,"path":311,"stem":312,"icon":313},"Glossary","\u002Fdocs\u002Freference\u002Fglossary","1.docs\u002F8.reference\u002F8.glossary","i-lucide-book-a",{"id":315,"title":120,"body":316,"description":4459,"extension":4460,"meta":4461,"navigation":4462,"pageType":4463,"path":121,"seo":4464,"stem":122,"__hash__":4465},"docs\u002F1.docs\u002F3.building\u002F4.aggregations.md",{"type":317,"value":318,"toc":4444},"minimark",[319,370,385,462,467,470,490,503,512,897,926,928,945,951,1361,1376,1378,1397,1409,1771,1804,1806,1815,1839,1855,1862,2696,2710,2733,2737,2770,2796,2801,2812,3022,3026,3037,3339,3343,3365,3886,3897,3901,3904,3987,3996,4000,4006,4041,4052,4056,4074,4394,4400,4404,4440],[320,321,322,323,327,328,332,333,336,337,340,341,344,345,348,349,352,353,356,357,360,361,348,364,356,366,369],"p",{},"Aggregation in StoatFlow follows the Kafka Streams shape: you first ",[324,325,326],"strong",{},"group"," records by key, then fold them into a running result with ",[329,330,331],"code",{},"count",", ",[329,334,335],{},"reduce",", or ",[329,338,339],{},"aggregate",". The result is a ",[329,342,343],{},"KTable\u003CK, R>"," — a changelog, one value per key, backed by a state store. This page covers grouping a ",[329,346,347],{},"KStream"," (",[329,350,351],{},"groupByKey"," \u002F ",[329,354,355],{},"groupBy"," → ",[329,358,359],{},"KGroupedStream",") and grouping a ",[329,362,363],{},"KTable",[329,365,355],{},[329,367,368],{},"KGroupedTable",", which adds the adder\u002Fsubtractor changelog form).",[320,371,372,373,376,377,380,381,384],{},"Windowed aggregations (",[329,374,375],{},"groupByKey().windowedBy(...)",") and co-grouping (",[329,378,379],{},"cogroup",") build on the same grouped types but have their own pages — see ",[382,383,125],"a",{"href":126},". Everything here is non-windowed.",[386,387,388],"tldr-panel",{},[389,390,391,406,431,447],"ul",{},[392,393,394,397,398,401,402,405],"li",{},[324,395,396],{},"Group first:"," ",[329,399,400],{},"groupByKey()"," keeps the key; ",[329,403,404],{},"groupBy(selector)"," re-keys (a sub-topology boundary).",[392,407,408,397,411,356,414,417,418,356,421,417,424,356,427,430],{},[324,409,410],{},"Then fold:",[329,412,413],{},"count()",[329,415,416],{},"KTable\u003CK, Long>","; ",[329,419,420],{},"reduce(reducer)",[329,422,423],{},"KTable\u003CK, V>",[329,425,426],{},"aggregate(initializer, aggregator)",[329,428,429],{},"KTable\u003CK, VR>",".",[392,432,433,436,437,440,441,444,445,430],{},[324,434,435],{},"Result is a state store."," Name and configure it with ",[329,438,439],{},"Materialized","; supply key\u002Fvalue serdes via ",[329,442,443],{},"Grouped"," or ",[329,446,439],{},[392,448,449,453,454,457,458,461],{},[324,450,451],{},[329,452,368],{}," (from ",[329,455,456],{},"KTable.groupBy",") takes an ",[324,459,460],{},"adder + subtractor"," so a re-keyed value is removed from its old group and added to its new one.",[463,464,466],"h2",{"id":465},"grouping-a-stream","Grouping a stream",[320,468,469],{},"Aggregation always starts from a grouped stream. Two ways to get one:",[389,471,472,479],{},[392,473,474,478],{},[324,475,476],{},[329,477,400],{}," — group by the record's current key. No re-keying, no sub-topology boundary.",[392,480,481,485,486,489],{},[324,482,483],{},[329,484,404],{}," — derive a new grouping key from each record. This is a key-changing operation: records are re-routed by the new key (StoatFlow does this in-memory between lanes — see ",[382,487,47],{"href":488},"\u002Fdocs\u002Fconcepts\u002Farchitecture#processing-lanes-and-key-affinity",").",[320,491,492,493,496,497,352,499,352,501,430],{},"Both return a ",[329,494,495],{},"KGroupedStream\u003CK, V>",", the entry point to ",[329,498,331],{},[329,500,335],{},[329,502,339],{},[320,504,505,506,508,509,511],{},"Serdes for the grouping key (and value, where it survives the fold) come from ",[329,507,443],{},". Provide them when the engine can't infer them from the source — most importantly after ",[329,510,355],{}," changes the key type, or when the value serde differs from the stream's default.",[513,514,516,674],"code-tabs",{"group":515},"lang",[517,518,524],"pre",{"className":519,"code":520,"filename":521,"language":522,"meta":523,"style":523},"language-kotlin shiki shiki-themes vitesse-light","import io.stoatflow.core.topology.Grouped\nimport org.apache.kafka.common.serialization.Serdes\n\n\u002F\u002F Group by current key\nval byKey = stream.groupByKey(Grouped.with(Serdes.String(), Serdes.String()))\n\n\u002F\u002F Re-key, then group (key type changes to the selector's return type)\nval byCategory =\n    stream.groupBy(\n        { _, product -> product.category },\n        Grouped.with(\"by-category\", Serdes.String(), productSerde),\n    )\n","Kotlin","kotlin","",[329,525,526,539,547,554,561,600,605,611,622,633,645,668],{"__ignoreMap":523},[527,528,531,535],"span",{"class":529,"line":530},"line",1,[527,532,534],{"class":533},"sbBg2","import",[527,536,538],{"class":537},"sySUi"," io.stoatflow.core.topology.Grouped\n",[527,540,542,544],{"class":529,"line":541},2,[527,543,534],{"class":533},[527,545,546],{"class":537}," org.apache.kafka.common.serialization.Serdes\n",[527,548,550],{"class":529,"line":549},3,[527,551,553],{"emptyLinePlaceholder":552},true,"\n",[527,555,557],{"class":529,"line":556},4,[527,558,560],{"class":559},"s8zF2","\u002F\u002F Group by current key\n",[527,562,564,567,571,575,578,580,583,586,589,592,595,597],{"class":529,"line":563},5,[527,565,566],{"class":533},"val",[527,568,570],{"class":569},"suHK_"," byKey ",[527,572,574],{"class":573},"sYZai","=",[527,576,577],{"class":569}," stream.",[527,579,351],{"class":537},[527,581,582],{"class":569},"(Grouped.",[527,584,585],{"class":537},"with",[527,587,588],{"class":569},"(Serdes.",[527,590,591],{"class":537},"String",[527,593,594],{"class":569},"(), Serdes.",[527,596,591],{"class":537},[527,598,599],{"class":569},"()))\n",[527,601,603],{"class":529,"line":602},6,[527,604,553],{"emptyLinePlaceholder":552},[527,606,608],{"class":529,"line":607},7,[527,609,610],{"class":559},"\u002F\u002F Re-key, then group (key type changes to the selector's return type)\n",[527,612,614,616,619],{"class":529,"line":613},8,[527,615,566],{"class":533},[527,617,618],{"class":569}," byCategory ",[527,620,621],{"class":573},"=\n",[527,623,625,628,630],{"class":529,"line":624},9,[527,626,627],{"class":569},"    stream.",[527,629,355],{"class":537},[527,631,632],{"class":569},"(\n",[527,634,636,639,642],{"class":529,"line":635},10,[527,637,638],{"class":569},"        { _, product ",[527,640,641],{"class":573},"->",[527,643,644],{"class":569}," product.category },\n",[527,646,648,651,653,656,660,663,665],{"class":529,"line":647},11,[527,649,650],{"class":569},"        Grouped.",[527,652,585],{"class":537},[527,654,655],{"class":569},"(",[527,657,659],{"class":658},"spphp","\"by-category\"",[527,661,662],{"class":569},", Serdes.",[527,664,591],{"class":537},[527,666,667],{"class":569},"(), productSerde),\n",[527,669,671],{"class":529,"line":670},12,[527,672,673],{"class":569},"    )\n",[517,675,680],{"className":676,"code":677,"filename":678,"language":679,"meta":523,"style":523},"language-java shiki shiki-themes vitesse-light","import io.stoatflow.core.topology.Grouped;\nimport org.apache.kafka.common.serialization.Serdes;\n\n\u002F\u002F Group by current key\nvar byKey = stream.groupByKey(Grouped.with(Serdes.String(), Serdes.String()));\n\n\u002F\u002F Re-key, then group (key type changes to the selector's return type)\nvar byCategory =\n    stream.groupBy(\n        (key, product) -> product.category(),\n        Grouped.with(\"by-category\", Serdes.String(), productSerde));\n","Java","java",[329,681,682,711,745,749,753,802,806,810,820,831,861],{"__ignoreMap":523},[527,683,684,686,689,691,694,696,699,701,704,706,708],{"class":529,"line":530},[527,685,534],{"class":533},[527,687,688],{"class":569}," io",[527,690,430],{"class":573},[527,692,693],{"class":569},"stoatflow",[527,695,430],{"class":573},[527,697,698],{"class":569},"core",[527,700,430],{"class":573},[527,702,703],{"class":569},"topology",[527,705,430],{"class":573},[527,707,443],{"class":569},[527,709,710],{"class":573},";\n",[527,712,713,715,718,720,723,725,728,730,733,735,738,740,743],{"class":529,"line":541},[527,714,534],{"class":533},[527,716,717],{"class":569}," org",[527,719,430],{"class":573},[527,721,722],{"class":569},"apache",[527,724,430],{"class":573},[527,726,727],{"class":569},"kafka",[527,729,430],{"class":573},[527,731,732],{"class":569},"common",[527,734,430],{"class":573},[527,736,737],{"class":569},"serialization",[527,739,430],{"class":573},[527,741,742],{"class":569},"Serdes",[527,744,710],{"class":573},[527,746,747],{"class":529,"line":549},[527,748,553],{"emptyLinePlaceholder":552},[527,750,751],{"class":529,"line":556},[527,752,560],{"class":559},[527,754,755,759,763,766,769,771,773,775,777,779,781,783,785,787,789,792,795,797,799],{"class":529,"line":563},[527,756,758],{"class":757},"si04Y","var",[527,760,762],{"class":761},"svycV"," byKey",[527,764,765],{"class":573}," =",[527,767,768],{"class":761}," stream",[527,770,430],{"class":573},[527,772,351],{"class":537},[527,774,655],{"class":573},[527,776,443],{"class":761},[527,778,430],{"class":573},[527,780,585],{"class":537},[527,782,655],{"class":573},[527,784,742],{"class":761},[527,786,430],{"class":573},[527,788,591],{"class":537},[527,790,791],{"class":573},"(),",[527,793,794],{"class":761}," Serdes",[527,796,430],{"class":573},[527,798,591],{"class":537},[527,800,801],{"class":573},"()));\n",[527,803,804],{"class":529,"line":602},[527,805,553],{"emptyLinePlaceholder":552},[527,807,808],{"class":529,"line":607},[527,809,610],{"class":559},[527,811,812,814,817],{"class":529,"line":613},[527,813,758],{"class":757},[527,815,816],{"class":761}," byCategory",[527,818,819],{"class":573}," =\n",[527,821,822,825,827,829],{"class":529,"line":624},[527,823,824],{"class":761},"    stream",[527,826,430],{"class":573},[527,828,355],{"class":537},[527,830,632],{"class":573},[527,832,833,836,839,842,845,848,851,853,855,858],{"class":529,"line":635},[527,834,835],{"class":573},"        (",[527,837,838],{"class":569},"key",[527,840,841],{"class":573},",",[527,843,844],{"class":569}," product",[527,846,847],{"class":573},")",[527,849,850],{"class":573}," ->",[527,852,844],{"class":761},[527,854,430],{"class":573},[527,856,857],{"class":537},"category",[527,859,860],{"class":573},"(),\n",[527,862,863,866,868,870,872,876,879,881,883,885,887,889,891,894],{"class":529,"line":647},[527,864,865],{"class":761},"        Grouped",[527,867,430],{"class":573},[527,869,585],{"class":537},[527,871,655],{"class":573},[527,873,875],{"class":874},"sSP4y","\"",[527,877,878],{"class":658},"by-category",[527,880,875],{"class":874},[527,882,841],{"class":573},[527,884,794],{"class":761},[527,886,430],{"class":573},[527,888,591],{"class":537},[527,890,791],{"class":573},[527,892,893],{"class":569}," productSerde",[527,895,896],{"class":573},"));\n",[898,899,902],"callout",{"color":900,"icon":901},"info","i-lucide-info",[320,903,904,906,907,332,910,332,913,332,916,332,919,922,923,925],{},[329,905,443],{}," factories: ",[329,908,909],{},"Grouped.with(keySerde, valueSerde)",[329,911,912],{},"Grouped.with(name, keySerde, valueSerde)",[329,914,915],{},"Grouped.as(name)",[329,917,918],{},"Grouped.keySerde(...)",[329,920,921],{},"Grouped.valueSerde(...)",". The name (when given) labels the grouping node in the topology graph and metrics; serdes set here are the default for the downstream aggregation unless ",[329,924,439],{}," overrides them.",[463,927,331],{"id":331},[320,929,930,932,933,935,936,939,940,332,942,944],{},[329,931,413],{}," returns a ",[329,934,416],{}," holding the number of records seen per key. The value serde is ",[329,937,938],{},"Long"," automatically; you only need a key serde (from ",[329,941,443],{},[329,943,439],{},", or the topology default). Null values (tombstones) are ignored and do not increment the count.",[320,946,947,948,950],{},"This is the word-count fold from ",[382,949,28],{"href":29},", in isolation:",[513,952,953,1100],{"group":515},[517,954,956],{"className":519,"code":955,"filename":521,"language":522,"meta":523,"style":523},"import io.stoatflow.core.state.StateStore\nimport io.stoatflow.core.topology.Grouped\nimport io.stoatflow.core.topology.Materialized\nimport io.stoatflow.core.topology.Named\n\nval counts: KTable\u003CString, Long> =\n    words\n        .groupBy({ _, word -> word }, Grouped.`as`(\"group-by-word\"))\n        .count(\n            Named.`as`(\"count\"),\n            Materialized.`as`\u003CString, Long, StateStore>(\"word-counts\"),\n        )\n",[329,957,958,965,971,978,985,989,1013,1018,1044,1052,1067,1095],{"__ignoreMap":523},[527,959,960,962],{"class":529,"line":530},[527,961,534],{"class":533},[527,963,964],{"class":537}," io.stoatflow.core.state.StateStore\n",[527,966,967,969],{"class":529,"line":541},[527,968,534],{"class":533},[527,970,538],{"class":537},[527,972,973,975],{"class":529,"line":549},[527,974,534],{"class":533},[527,976,977],{"class":537}," io.stoatflow.core.topology.Materialized\n",[527,979,980,982],{"class":529,"line":556},[527,981,534],{"class":533},[527,983,984],{"class":537}," io.stoatflow.core.topology.Named\n",[527,986,987],{"class":529,"line":563},[527,988,553],{"emptyLinePlaceholder":552},[527,990,991,993,996,999,1002,1004,1006,1008,1011],{"class":529,"line":602},[527,992,566],{"class":533},[527,994,995],{"class":569}," counts: ",[527,997,363],{"class":998},"sUxyF",[527,1000,1001],{"class":569},"\u003C",[527,1003,591],{"class":998},[527,1005,332],{"class":569},[527,1007,938],{"class":998},[527,1009,1010],{"class":569},"> ",[527,1012,621],{"class":573},[527,1014,1015],{"class":529,"line":607},[527,1016,1017],{"class":569},"    words\n",[527,1019,1020,1023,1025,1028,1030,1033,1036,1038,1041],{"class":529,"line":613},[527,1021,1022],{"class":569},"        .",[527,1024,355],{"class":537},[527,1026,1027],{"class":569},"({ _, word ",[527,1029,641],{"class":573},[527,1031,1032],{"class":569}," word }, Grouped.",[527,1034,1035],{"class":537},"`as`",[527,1037,655],{"class":569},[527,1039,1040],{"class":658},"\"group-by-word\"",[527,1042,1043],{"class":569},"))\n",[527,1045,1046,1048,1050],{"class":529,"line":624},[527,1047,1022],{"class":569},[527,1049,331],{"class":537},[527,1051,632],{"class":569},[527,1053,1054,1057,1059,1061,1064],{"class":529,"line":635},[527,1055,1056],{"class":569},"            Named.",[527,1058,1035],{"class":537},[527,1060,655],{"class":569},[527,1062,1063],{"class":658},"\"count\"",[527,1065,1066],{"class":569},"),\n",[527,1068,1069,1072,1074,1076,1078,1080,1082,1084,1087,1090,1093],{"class":529,"line":647},[527,1070,1071],{"class":569},"            Materialized.",[527,1073,1035],{"class":537},[527,1075,1001],{"class":569},[527,1077,591],{"class":998},[527,1079,332],{"class":569},[527,1081,938],{"class":998},[527,1083,332],{"class":569},[527,1085,1086],{"class":998},"StateStore",[527,1088,1089],{"class":569},">(",[527,1091,1092],{"class":658},"\"word-counts\"",[527,1094,1066],{"class":569},[527,1096,1097],{"class":529,"line":670},[527,1098,1099],{"class":569},"        )\n",[517,1101,1103],{"className":676,"code":1102,"filename":678,"language":679,"meta":523,"style":523},"import io.stoatflow.core.state.StateStore;\nimport io.stoatflow.core.topology.Grouped;\nimport io.stoatflow.core.topology.KTable;\nimport io.stoatflow.core.topology.Materialized;\nimport io.stoatflow.core.topology.Named;\n\nKTable\u003CString, Long> counts =\n    words\n        .groupBy((key, word) -> word, Grouped.as(\"group-by-word\"))\n        .count(\n            Named.as(\"count\"),\n            Materialized.\u003CString, Long, StateStore>as(\"word-counts\"));\n",[329,1104,1105,1130,1154,1178,1202,1227,1231,1252,1256,1299,1307,1326],{"__ignoreMap":523},[527,1106,1107,1109,1111,1113,1115,1117,1119,1121,1124,1126,1128],{"class":529,"line":530},[527,1108,534],{"class":533},[527,1110,688],{"class":569},[527,1112,430],{"class":573},[527,1114,693],{"class":569},[527,1116,430],{"class":573},[527,1118,698],{"class":569},[527,1120,430],{"class":573},[527,1122,1123],{"class":569},"state",[527,1125,430],{"class":573},[527,1127,1086],{"class":569},[527,1129,710],{"class":573},[527,1131,1132,1134,1136,1138,1140,1142,1144,1146,1148,1150,1152],{"class":529,"line":541},[527,1133,534],{"class":533},[527,1135,688],{"class":569},[527,1137,430],{"class":573},[527,1139,693],{"class":569},[527,1141,430],{"class":573},[527,1143,698],{"class":569},[527,1145,430],{"class":573},[527,1147,703],{"class":569},[527,1149,430],{"class":573},[527,1151,443],{"class":569},[527,1153,710],{"class":573},[527,1155,1156,1158,1160,1162,1164,1166,1168,1170,1172,1174,1176],{"class":529,"line":549},[527,1157,534],{"class":533},[527,1159,688],{"class":569},[527,1161,430],{"class":573},[527,1163,693],{"class":569},[527,1165,430],{"class":573},[527,1167,698],{"class":569},[527,1169,430],{"class":573},[527,1171,703],{"class":569},[527,1173,430],{"class":573},[527,1175,363],{"class":569},[527,1177,710],{"class":573},[527,1179,1180,1182,1184,1186,1188,1190,1192,1194,1196,1198,1200],{"class":529,"line":556},[527,1181,534],{"class":533},[527,1183,688],{"class":569},[527,1185,430],{"class":573},[527,1187,693],{"class":569},[527,1189,430],{"class":573},[527,1191,698],{"class":569},[527,1193,430],{"class":573},[527,1195,703],{"class":569},[527,1197,430],{"class":573},[527,1199,439],{"class":569},[527,1201,710],{"class":573},[527,1203,1204,1206,1208,1210,1212,1214,1216,1218,1220,1222,1225],{"class":529,"line":563},[527,1205,534],{"class":533},[527,1207,688],{"class":569},[527,1209,430],{"class":573},[527,1211,693],{"class":569},[527,1213,430],{"class":573},[527,1215,698],{"class":569},[527,1217,430],{"class":573},[527,1219,703],{"class":569},[527,1221,430],{"class":573},[527,1223,1224],{"class":569},"Named",[527,1226,710],{"class":573},[527,1228,1229],{"class":529,"line":602},[527,1230,553],{"emptyLinePlaceholder":552},[527,1232,1233,1235,1237,1239,1241,1244,1247,1250],{"class":529,"line":607},[527,1234,363],{"class":569},[527,1236,1001],{"class":573},[527,1238,591],{"class":757},[527,1240,841],{"class":573},[527,1242,1243],{"class":757}," Long",[527,1245,1246],{"class":573},">",[527,1248,1249],{"class":761}," counts",[527,1251,819],{"class":573},[527,1253,1254],{"class":529,"line":613},[527,1255,1017],{"class":569},[527,1257,1258,1260,1262,1265,1267,1269,1272,1274,1276,1278,1280,1283,1285,1288,1290,1292,1295,1297],{"class":529,"line":624},[527,1259,1022],{"class":573},[527,1261,355],{"class":537},[527,1263,1264],{"class":573},"((",[527,1266,838],{"class":569},[527,1268,841],{"class":573},[527,1270,1271],{"class":569}," word",[527,1273,847],{"class":573},[527,1275,850],{"class":573},[527,1277,1271],{"class":569},[527,1279,841],{"class":573},[527,1281,1282],{"class":761}," Grouped",[527,1284,430],{"class":573},[527,1286,1287],{"class":537},"as",[527,1289,655],{"class":573},[527,1291,875],{"class":874},[527,1293,1294],{"class":658},"group-by-word",[527,1296,875],{"class":874},[527,1298,1043],{"class":573},[527,1300,1301,1303,1305],{"class":529,"line":635},[527,1302,1022],{"class":573},[527,1304,331],{"class":537},[527,1306,632],{"class":573},[527,1308,1309,1312,1314,1316,1318,1320,1322,1324],{"class":529,"line":647},[527,1310,1311],{"class":761},"            Named",[527,1313,430],{"class":573},[527,1315,1287],{"class":537},[527,1317,655],{"class":573},[527,1319,875],{"class":874},[527,1321,331],{"class":658},[527,1323,875],{"class":874},[527,1325,1066],{"class":573},[527,1327,1328,1331,1333,1335,1337,1339,1341,1343,1346,1348,1350,1352,1354,1357,1359],{"class":529,"line":670},[527,1329,1330],{"class":569},"            Materialized",[527,1332,430],{"class":573},[527,1334,1001],{"class":757},[527,1336,591],{"class":569},[527,1338,841],{"class":573},[527,1340,1243],{"class":569},[527,1342,841],{"class":573},[527,1344,1345],{"class":569}," StateStore",[527,1347,1246],{"class":757},[527,1349,1287],{"class":537},[527,1351,655],{"class":573},[527,1353,875],{"class":874},[527,1355,1356],{"class":658},"word-counts",[527,1358,875],{"class":874},[527,1360,896],{"class":573},[320,1362,1363,1365,1366,332,1369,332,1372,1375],{},[329,1364,413],{}," has overloads for ",[329,1367,1368],{},"(Named)",[329,1370,1371],{},"(Materialized)",[329,1373,1374],{},"(Named, Materialized)",", and the no-arg form (auto-named store) — pass only what you need.",[463,1377,335],{"id":335},[320,1379,1380,1382,1383,1386,1387,1390,1391,340,1394,1396],{},[329,1381,420],{}," folds values of the ",[324,1384,1385],{},"same type"," into one per key. The first value for a key is stored as-is; each subsequent value is combined with the stored aggregate via the ",[329,1388,1389],{},"Reducer\u003CV>"," — ",[329,1392,1393],{},"(aggregate, value) -> aggregate",[329,1395,423],{},". Null values are ignored.",[320,1398,1399,1400,1402,1403,1405,1406,1408],{},"Use ",[329,1401,335],{}," when the running result has the same type as the input (sum, max, latest-wins, string concatenation). The value serde carries through, so it comes from ",[329,1404,443],{},"\u002F",[329,1407,439],{},"\u002Fthe default.",[513,1410,1411,1545],{"group":515},[517,1412,1414],{"className":519,"code":1413,"filename":521,"language":522,"meta":523,"style":523},"import io.stoatflow.core.state.StateStore\nimport io.stoatflow.core.topology.Materialized\nimport io.stoatflow.core.topology.Named\n\n\u002F\u002F Running total of order amounts per customer\nval totals: KTable\u003CString, Long> =\n    orderAmounts\n        .groupByKey()\n        .reduce(\n            { runningTotal, amount -> runningTotal + amount },\n            Named.`as`(\"sum-amounts\"),\n            Materialized.`as`\u003CString, Long, StateStore>(\"amount-totals\"),\n        )\n",[329,1415,1416,1422,1428,1434,1438,1443,1464,1469,1478,1486,1502,1515,1540],{"__ignoreMap":523},[527,1417,1418,1420],{"class":529,"line":530},[527,1419,534],{"class":533},[527,1421,964],{"class":537},[527,1423,1424,1426],{"class":529,"line":541},[527,1425,534],{"class":533},[527,1427,977],{"class":537},[527,1429,1430,1432],{"class":529,"line":549},[527,1431,534],{"class":533},[527,1433,984],{"class":537},[527,1435,1436],{"class":529,"line":556},[527,1437,553],{"emptyLinePlaceholder":552},[527,1439,1440],{"class":529,"line":563},[527,1441,1442],{"class":559},"\u002F\u002F Running total of order amounts per customer\n",[527,1444,1445,1447,1450,1452,1454,1456,1458,1460,1462],{"class":529,"line":602},[527,1446,566],{"class":533},[527,1448,1449],{"class":569}," totals: ",[527,1451,363],{"class":998},[527,1453,1001],{"class":569},[527,1455,591],{"class":998},[527,1457,332],{"class":569},[527,1459,938],{"class":998},[527,1461,1010],{"class":569},[527,1463,621],{"class":573},[527,1465,1466],{"class":529,"line":607},[527,1467,1468],{"class":569},"    orderAmounts\n",[527,1470,1471,1473,1475],{"class":529,"line":613},[527,1472,1022],{"class":569},[527,1474,351],{"class":537},[527,1476,1477],{"class":569},"()\n",[527,1479,1480,1482,1484],{"class":529,"line":624},[527,1481,1022],{"class":569},[527,1483,335],{"class":537},[527,1485,632],{"class":569},[527,1487,1488,1491,1493,1496,1499],{"class":529,"line":635},[527,1489,1490],{"class":569},"            { runningTotal, amount ",[527,1492,641],{"class":573},[527,1494,1495],{"class":569}," runningTotal ",[527,1497,1498],{"class":757},"+",[527,1500,1501],{"class":569}," amount },\n",[527,1503,1504,1506,1508,1510,1513],{"class":529,"line":647},[527,1505,1056],{"class":569},[527,1507,1035],{"class":537},[527,1509,655],{"class":569},[527,1511,1512],{"class":658},"\"sum-amounts\"",[527,1514,1066],{"class":569},[527,1516,1517,1519,1521,1523,1525,1527,1529,1531,1533,1535,1538],{"class":529,"line":670},[527,1518,1071],{"class":569},[527,1520,1035],{"class":537},[527,1522,1001],{"class":569},[527,1524,591],{"class":998},[527,1526,332],{"class":569},[527,1528,938],{"class":998},[527,1530,332],{"class":569},[527,1532,1086],{"class":998},[527,1534,1089],{"class":569},[527,1536,1537],{"class":658},"\"amount-totals\"",[527,1539,1066],{"class":569},[527,1541,1543],{"class":529,"line":1542},13,[527,1544,1099],{"class":569},[517,1546,1548],{"className":676,"code":1547,"filename":678,"language":679,"meta":523,"style":523},"import io.stoatflow.core.state.StateStore;\nimport io.stoatflow.core.topology.KTable;\nimport io.stoatflow.core.topology.Materialized;\nimport io.stoatflow.core.topology.Named;\n\n\u002F\u002F Running total of order amounts per customer\nKTable\u003CString, Long> totals =\n    orderAmounts\n        .groupByKey()\n        .reduce(\n            (runningTotal, amount) -> runningTotal + amount,\n            Named.as(\"sum-amounts\"),\n            Materialized.\u003CString, Long, StateStore>as(\"amount-totals\"));\n",[329,1549,1550,1574,1598,1622,1646,1650,1654,1673,1677,1685,1693,1719,1738],{"__ignoreMap":523},[527,1551,1552,1554,1556,1558,1560,1562,1564,1566,1568,1570,1572],{"class":529,"line":530},[527,1553,534],{"class":533},[527,1555,688],{"class":569},[527,1557,430],{"class":573},[527,1559,693],{"class":569},[527,1561,430],{"class":573},[527,1563,698],{"class":569},[527,1565,430],{"class":573},[527,1567,1123],{"class":569},[527,1569,430],{"class":573},[527,1571,1086],{"class":569},[527,1573,710],{"class":573},[527,1575,1576,1578,1580,1582,1584,1586,1588,1590,1592,1594,1596],{"class":529,"line":541},[527,1577,534],{"class":533},[527,1579,688],{"class":569},[527,1581,430],{"class":573},[527,1583,693],{"class":569},[527,1585,430],{"class":573},[527,1587,698],{"class":569},[527,1589,430],{"class":573},[527,1591,703],{"class":569},[527,1593,430],{"class":573},[527,1595,363],{"class":569},[527,1597,710],{"class":573},[527,1599,1600,1602,1604,1606,1608,1610,1612,1614,1616,1618,1620],{"class":529,"line":549},[527,1601,534],{"class":533},[527,1603,688],{"class":569},[527,1605,430],{"class":573},[527,1607,693],{"class":569},[527,1609,430],{"class":573},[527,1611,698],{"class":569},[527,1613,430],{"class":573},[527,1615,703],{"class":569},[527,1617,430],{"class":573},[527,1619,439],{"class":569},[527,1621,710],{"class":573},[527,1623,1624,1626,1628,1630,1632,1634,1636,1638,1640,1642,1644],{"class":529,"line":556},[527,1625,534],{"class":533},[527,1627,688],{"class":569},[527,1629,430],{"class":573},[527,1631,693],{"class":569},[527,1633,430],{"class":573},[527,1635,698],{"class":569},[527,1637,430],{"class":573},[527,1639,703],{"class":569},[527,1641,430],{"class":573},[527,1643,1224],{"class":569},[527,1645,710],{"class":573},[527,1647,1648],{"class":529,"line":563},[527,1649,553],{"emptyLinePlaceholder":552},[527,1651,1652],{"class":529,"line":602},[527,1653,1442],{"class":559},[527,1655,1656,1658,1660,1662,1664,1666,1668,1671],{"class":529,"line":607},[527,1657,363],{"class":569},[527,1659,1001],{"class":573},[527,1661,591],{"class":757},[527,1663,841],{"class":573},[527,1665,1243],{"class":757},[527,1667,1246],{"class":573},[527,1669,1670],{"class":761}," totals",[527,1672,819],{"class":573},[527,1674,1675],{"class":529,"line":613},[527,1676,1468],{"class":569},[527,1678,1679,1681,1683],{"class":529,"line":624},[527,1680,1022],{"class":573},[527,1682,351],{"class":537},[527,1684,1477],{"class":573},[527,1686,1687,1689,1691],{"class":529,"line":635},[527,1688,1022],{"class":573},[527,1690,335],{"class":537},[527,1692,632],{"class":573},[527,1694,1695,1698,1701,1703,1706,1708,1710,1712,1714,1716],{"class":529,"line":647},[527,1696,1697],{"class":573},"            (",[527,1699,1700],{"class":569},"runningTotal",[527,1702,841],{"class":573},[527,1704,1705],{"class":569}," amount",[527,1707,847],{"class":573},[527,1709,850],{"class":573},[527,1711,1495],{"class":569},[527,1713,1498],{"class":757},[527,1715,1705],{"class":569},[527,1717,1718],{"class":573},",\n",[527,1720,1721,1723,1725,1727,1729,1731,1734,1736],{"class":529,"line":670},[527,1722,1311],{"class":761},[527,1724,430],{"class":573},[527,1726,1287],{"class":537},[527,1728,655],{"class":573},[527,1730,875],{"class":874},[527,1732,1733],{"class":658},"sum-amounts",[527,1735,875],{"class":874},[527,1737,1066],{"class":573},[527,1739,1740,1742,1744,1746,1748,1750,1752,1754,1756,1758,1760,1762,1764,1767,1769],{"class":529,"line":1542},[527,1741,1330],{"class":569},[527,1743,430],{"class":573},[527,1745,1001],{"class":757},[527,1747,591],{"class":569},[527,1749,841],{"class":573},[527,1751,1243],{"class":569},[527,1753,841],{"class":573},[527,1755,1345],{"class":569},[527,1757,1246],{"class":757},[527,1759,1287],{"class":537},[527,1761,655],{"class":573},[527,1763,875],{"class":874},[527,1765,1766],{"class":658},"amount-totals",[527,1768,875],{"class":874},[527,1770,896],{"class":573},[898,1772,1773],{"color":900,"icon":901},[320,1774,1775,1776,1778,1779,352,1781,1783,1784,1786,1787,1790,1791,332,1794,332,1797,1800,1801,430],{},"In Kotlin, ",[329,1777,335],{}," takes the reducer lambda first, then optional ",[329,1780,1224],{},[329,1782,439],{},". In Java, the reducer is a ",[329,1785,1389],{}," — the SAM type for ",[329,1788,1789],{},"(V, V) -> V"," — so a lambda passed inline converts to it directly. Overloads accept ",[329,1792,1793],{},"(Reducer)",[329,1795,1796],{},"(Reducer, Named)",[329,1798,1799],{},"(Reducer, Materialized)",", and ",[329,1802,1803],{},"(Reducer, Named, Materialized)",[463,1805,339],{"id":339},[320,1807,1808,1810,1811,1814],{},[329,1809,339],{}," is the general fold: the result type ",[329,1812,1813],{},"VR"," is independent of the input value type. You supply two functions:",[389,1816,1817,1828],{},[392,1818,1819,1390,1824,1827],{},[324,1820,1821],{},[329,1822,1823],{},"Initializer\u003CVR>",[329,1825,1826],{},"() -> VR",", the starting aggregate for a key not seen before.",[392,1829,1830,1390,1835,1838],{},[324,1831,1832],{},[329,1833,1834],{},"Aggregator\u003CK, V, VR>",[329,1836,1837],{},"(key, value, aggregate) -> aggregate",", applied for each record.",[320,1840,1841,1842,1844,1845,1848,1849,1852,1853,1408],{},"The result is a ",[329,1843,429],{},". Because the aggregate type differs from the value type, give it a value serde via ",[329,1846,1847],{},"Materialized.withValueSerde(...)"," (or ",[329,1850,1851],{},"Materialized.with(keySerde, valueSerde)","); the key serde still falls back to ",[329,1854,443],{},[320,1856,1857,1858,1861],{},"This mirrors the per-customer daily aggregation in the e-commerce example (",[329,1859,1860],{},"examples\u002Fecommerce-daily-customer-behaviour","), simplified to a non-windowed running aggregate:",[513,1863,1864,2227],{"group":515},[517,1865,1867],{"className":519,"code":1866,"filename":521,"language":522,"meta":523,"style":523},"import io.stoatflow.core.state.StateStore\nimport io.stoatflow.core.topology.Grouped\nimport io.stoatflow.core.topology.Materialized\nimport io.stoatflow.core.topology.Named\nimport org.apache.kafka.common.serialization.Serdes\n\ndata class DailyAggregates(\n    val productsViewed: Int,\n    val purchases: Int,\n    val amountSpent: Double,\n)\n\nval perCustomer: KTable\u003CString, DailyAggregates> =\n    events\n        .groupBy(\n            { _, e -> e.customerId },\n            Grouped.with(\"by-customer\", Serdes.String(), eventSerde),\n        )\n        .aggregate(\n            { DailyAggregates(0, 0, 0.0) },\n            { _, event, agg ->\n                DailyAggregates(\n                    productsViewed = agg.productsViewed + if (event.isView) 1 else 0,\n                    purchases = agg.purchases + if (event.isPurchase) 1 else 0,\n                    amountSpent = agg.amountSpent + event.amount,\n                )\n            },\n            Named.`as`(\"daily-aggregation\"),\n            Materialized.`as`\u003CString, DailyAggregates, StateStore>(\"daily-aggregation-store\")\n                .withValueSerde(dailyAggregatesSerde),\n        )\n",[329,1868,1869,1875,1881,1887,1893,1899,1903,1916,1929,1940,1952,1957,1961,1983,1989,1998,2009,2029,2034,2043,2069,2078,2086,2116,2142,2158,2164,2170,2184,2210,2222],{"__ignoreMap":523},[527,1870,1871,1873],{"class":529,"line":530},[527,1872,534],{"class":533},[527,1874,964],{"class":537},[527,1876,1877,1879],{"class":529,"line":541},[527,1878,534],{"class":533},[527,1880,538],{"class":537},[527,1882,1883,1885],{"class":529,"line":549},[527,1884,534],{"class":533},[527,1886,977],{"class":537},[527,1888,1889,1891],{"class":529,"line":556},[527,1890,534],{"class":533},[527,1892,984],{"class":537},[527,1894,1895,1897],{"class":529,"line":563},[527,1896,534],{"class":533},[527,1898,546],{"class":537},[527,1900,1901],{"class":529,"line":602},[527,1902,553],{"emptyLinePlaceholder":552},[527,1904,1905,1908,1911,1914],{"class":529,"line":607},[527,1906,1907],{"class":757},"data",[527,1909,1910],{"class":533}," class",[527,1912,1913],{"class":998}," DailyAggregates",[527,1915,632],{"class":569},[527,1917,1918,1921,1924,1927],{"class":529,"line":613},[527,1919,1920],{"class":533},"    val",[527,1922,1923],{"class":569}," productsViewed: ",[527,1925,1926],{"class":998},"Int",[527,1928,1718],{"class":569},[527,1930,1931,1933,1936,1938],{"class":529,"line":624},[527,1932,1920],{"class":533},[527,1934,1935],{"class":569}," purchases: ",[527,1937,1926],{"class":998},[527,1939,1718],{"class":569},[527,1941,1942,1944,1947,1950],{"class":529,"line":635},[527,1943,1920],{"class":533},[527,1945,1946],{"class":569}," amountSpent: ",[527,1948,1949],{"class":998},"Double",[527,1951,1718],{"class":569},[527,1953,1954],{"class":529,"line":647},[527,1955,1956],{"class":569},")\n",[527,1958,1959],{"class":529,"line":670},[527,1960,553],{"emptyLinePlaceholder":552},[527,1962,1963,1965,1968,1970,1972,1974,1976,1979,1981],{"class":529,"line":1542},[527,1964,566],{"class":533},[527,1966,1967],{"class":569}," perCustomer: ",[527,1969,363],{"class":998},[527,1971,1001],{"class":569},[527,1973,591],{"class":998},[527,1975,332],{"class":569},[527,1977,1978],{"class":998},"DailyAggregates",[527,1980,1010],{"class":569},[527,1982,621],{"class":573},[527,1984,1986],{"class":529,"line":1985},14,[527,1987,1988],{"class":569},"    events\n",[527,1990,1992,1994,1996],{"class":529,"line":1991},15,[527,1993,1022],{"class":569},[527,1995,355],{"class":537},[527,1997,632],{"class":569},[527,1999,2001,2004,2006],{"class":529,"line":2000},16,[527,2002,2003],{"class":569},"            { _, e ",[527,2005,641],{"class":573},[527,2007,2008],{"class":569}," e.customerId },\n",[527,2010,2012,2015,2017,2019,2022,2024,2026],{"class":529,"line":2011},17,[527,2013,2014],{"class":569},"            Grouped.",[527,2016,585],{"class":537},[527,2018,655],{"class":569},[527,2020,2021],{"class":658},"\"by-customer\"",[527,2023,662],{"class":569},[527,2025,591],{"class":537},[527,2027,2028],{"class":569},"(), eventSerde),\n",[527,2030,2032],{"class":529,"line":2031},18,[527,2033,1099],{"class":569},[527,2035,2037,2039,2041],{"class":529,"line":2036},19,[527,2038,1022],{"class":569},[527,2040,339],{"class":537},[527,2042,632],{"class":569},[527,2044,2046,2049,2051,2053,2057,2059,2061,2063,2066],{"class":529,"line":2045},20,[527,2047,2048],{"class":569},"            { ",[527,2050,1978],{"class":537},[527,2052,655],{"class":569},[527,2054,2056],{"class":2055},"s-TwI","0",[527,2058,332],{"class":569},[527,2060,2056],{"class":2055},[527,2062,332],{"class":569},[527,2064,2065],{"class":2055},"0.0",[527,2067,2068],{"class":569},") },\n",[527,2070,2072,2075],{"class":529,"line":2071},21,[527,2073,2074],{"class":569},"            { _, event, agg ",[527,2076,2077],{"class":573},"->\n",[527,2079,2081,2084],{"class":529,"line":2080},22,[527,2082,2083],{"class":537},"                DailyAggregates",[527,2085,632],{"class":569},[527,2087,2089,2092,2094,2097,2099,2102,2105,2108,2111,2114],{"class":529,"line":2088},23,[527,2090,2091],{"class":569},"                    productsViewed ",[527,2093,574],{"class":573},[527,2095,2096],{"class":569}," agg.productsViewed ",[527,2098,1498],{"class":757},[527,2100,2101],{"class":533}," if",[527,2103,2104],{"class":569}," (event.isView) ",[527,2106,2107],{"class":2055},"1",[527,2109,2110],{"class":533}," else",[527,2112,2113],{"class":2055}," 0",[527,2115,1718],{"class":569},[527,2117,2119,2122,2124,2127,2129,2131,2134,2136,2138,2140],{"class":529,"line":2118},24,[527,2120,2121],{"class":569},"                    purchases ",[527,2123,574],{"class":573},[527,2125,2126],{"class":569}," agg.purchases ",[527,2128,1498],{"class":757},[527,2130,2101],{"class":533},[527,2132,2133],{"class":569}," (event.isPurchase) ",[527,2135,2107],{"class":2055},[527,2137,2110],{"class":533},[527,2139,2113],{"class":2055},[527,2141,1718],{"class":569},[527,2143,2145,2148,2150,2153,2155],{"class":529,"line":2144},25,[527,2146,2147],{"class":569},"                    amountSpent ",[527,2149,574],{"class":573},[527,2151,2152],{"class":569}," agg.amountSpent ",[527,2154,1498],{"class":757},[527,2156,2157],{"class":569}," event.amount,\n",[527,2159,2161],{"class":529,"line":2160},26,[527,2162,2163],{"class":569},"                )\n",[527,2165,2167],{"class":529,"line":2166},27,[527,2168,2169],{"class":569},"            },\n",[527,2171,2173,2175,2177,2179,2182],{"class":529,"line":2172},28,[527,2174,1056],{"class":569},[527,2176,1035],{"class":537},[527,2178,655],{"class":569},[527,2180,2181],{"class":658},"\"daily-aggregation\"",[527,2183,1066],{"class":569},[527,2185,2187,2189,2191,2193,2195,2197,2199,2201,2203,2205,2208],{"class":529,"line":2186},29,[527,2188,1071],{"class":569},[527,2190,1035],{"class":537},[527,2192,1001],{"class":569},[527,2194,591],{"class":998},[527,2196,332],{"class":569},[527,2198,1978],{"class":998},[527,2200,332],{"class":569},[527,2202,1086],{"class":998},[527,2204,1089],{"class":569},[527,2206,2207],{"class":658},"\"daily-aggregation-store\"",[527,2209,1956],{"class":569},[527,2211,2213,2216,2219],{"class":529,"line":2212},30,[527,2214,2215],{"class":569},"                .",[527,2217,2218],{"class":537},"withValueSerde",[527,2220,2221],{"class":569},"(dailyAggregatesSerde),\n",[527,2223,2225],{"class":529,"line":2224},31,[527,2226,1099],{"class":569},[517,2228,2230],{"className":676,"code":2229,"filename":678,"language":679,"meta":523,"style":523},"import io.stoatflow.core.state.StateStore;\nimport io.stoatflow.core.topology.Grouped;\nimport io.stoatflow.core.topology.KTable;\nimport io.stoatflow.core.topology.Materialized;\nimport io.stoatflow.core.topology.Named;\nimport org.apache.kafka.common.serialization.Serdes;\n\nKTable\u003CString, DailyAggregates> perCustomer =\n    events\n        .groupBy(\n            (key, e) -> e.customerId(),\n            Grouped.with(\"by-customer\", Serdes.String(), eventSerde))\n        .aggregate(\n            () -> new DailyAggregates(0, 0, 0.0),\n            (key, event, agg) -> new DailyAggregates(\n                agg.productsViewed() + (event.isView() ? 1 : 0),\n                agg.purchases() + (event.isPurchase() ? 1 : 0),\n                agg.amountSpent() + event.amount()),\n            Named.as(\"daily-aggregation\"),\n            Materialized.\u003CString, DailyAggregates, StateStore>as(\"daily-aggregation-store\")\n                .withValueSerde(dailyAggregatesSerde));\n",[329,2231,2232,2256,2280,2304,2328,2352,2380,2384,2403,2407,2415,2439,2472,2480,2507,2533,2574,2608,2631,2650,2683],{"__ignoreMap":523},[527,2233,2234,2236,2238,2240,2242,2244,2246,2248,2250,2252,2254],{"class":529,"line":530},[527,2235,534],{"class":533},[527,2237,688],{"class":569},[527,2239,430],{"class":573},[527,2241,693],{"class":569},[527,2243,430],{"class":573},[527,2245,698],{"class":569},[527,2247,430],{"class":573},[527,2249,1123],{"class":569},[527,2251,430],{"class":573},[527,2253,1086],{"class":569},[527,2255,710],{"class":573},[527,2257,2258,2260,2262,2264,2266,2268,2270,2272,2274,2276,2278],{"class":529,"line":541},[527,2259,534],{"class":533},[527,2261,688],{"class":569},[527,2263,430],{"class":573},[527,2265,693],{"class":569},[527,2267,430],{"class":573},[527,2269,698],{"class":569},[527,2271,430],{"class":573},[527,2273,703],{"class":569},[527,2275,430],{"class":573},[527,2277,443],{"class":569},[527,2279,710],{"class":573},[527,2281,2282,2284,2286,2288,2290,2292,2294,2296,2298,2300,2302],{"class":529,"line":549},[527,2283,534],{"class":533},[527,2285,688],{"class":569},[527,2287,430],{"class":573},[527,2289,693],{"class":569},[527,2291,430],{"class":573},[527,2293,698],{"class":569},[527,2295,430],{"class":573},[527,2297,703],{"class":569},[527,2299,430],{"class":573},[527,2301,363],{"class":569},[527,2303,710],{"class":573},[527,2305,2306,2308,2310,2312,2314,2316,2318,2320,2322,2324,2326],{"class":529,"line":556},[527,2307,534],{"class":533},[527,2309,688],{"class":569},[527,2311,430],{"class":573},[527,2313,693],{"class":569},[527,2315,430],{"class":573},[527,2317,698],{"class":569},[527,2319,430],{"class":573},[527,2321,703],{"class":569},[527,2323,430],{"class":573},[527,2325,439],{"class":569},[527,2327,710],{"class":573},[527,2329,2330,2332,2334,2336,2338,2340,2342,2344,2346,2348,2350],{"class":529,"line":563},[527,2331,534],{"class":533},[527,2333,688],{"class":569},[527,2335,430],{"class":573},[527,2337,693],{"class":569},[527,2339,430],{"class":573},[527,2341,698],{"class":569},[527,2343,430],{"class":573},[527,2345,703],{"class":569},[527,2347,430],{"class":573},[527,2349,1224],{"class":569},[527,2351,710],{"class":573},[527,2353,2354,2356,2358,2360,2362,2364,2366,2368,2370,2372,2374,2376,2378],{"class":529,"line":602},[527,2355,534],{"class":533},[527,2357,717],{"class":569},[527,2359,430],{"class":573},[527,2361,722],{"class":569},[527,2363,430],{"class":573},[527,2365,727],{"class":569},[527,2367,430],{"class":573},[527,2369,732],{"class":569},[527,2371,430],{"class":573},[527,2373,737],{"class":569},[527,2375,430],{"class":573},[527,2377,742],{"class":569},[527,2379,710],{"class":573},[527,2381,2382],{"class":529,"line":607},[527,2383,553],{"emptyLinePlaceholder":552},[527,2385,2386,2388,2390,2392,2394,2396,2398,2401],{"class":529,"line":613},[527,2387,363],{"class":569},[527,2389,1001],{"class":573},[527,2391,591],{"class":757},[527,2393,841],{"class":573},[527,2395,1913],{"class":757},[527,2397,1246],{"class":573},[527,2399,2400],{"class":761}," perCustomer",[527,2402,819],{"class":573},[527,2404,2405],{"class":529,"line":624},[527,2406,1988],{"class":569},[527,2408,2409,2411,2413],{"class":529,"line":635},[527,2410,1022],{"class":573},[527,2412,355],{"class":537},[527,2414,632],{"class":573},[527,2416,2417,2419,2421,2423,2426,2428,2430,2432,2434,2437],{"class":529,"line":647},[527,2418,1697],{"class":573},[527,2420,838],{"class":569},[527,2422,841],{"class":573},[527,2424,2425],{"class":569}," e",[527,2427,847],{"class":573},[527,2429,850],{"class":573},[527,2431,2425],{"class":761},[527,2433,430],{"class":573},[527,2435,2436],{"class":537},"customerId",[527,2438,860],{"class":573},[527,2440,2441,2444,2446,2448,2450,2452,2455,2457,2459,2461,2463,2465,2467,2470],{"class":529,"line":670},[527,2442,2443],{"class":761},"            Grouped",[527,2445,430],{"class":573},[527,2447,585],{"class":537},[527,2449,655],{"class":573},[527,2451,875],{"class":874},[527,2453,2454],{"class":658},"by-customer",[527,2456,875],{"class":874},[527,2458,841],{"class":573},[527,2460,794],{"class":761},[527,2462,430],{"class":573},[527,2464,591],{"class":537},[527,2466,791],{"class":573},[527,2468,2469],{"class":569}," eventSerde",[527,2471,1043],{"class":573},[527,2473,2474,2476,2478],{"class":529,"line":1542},[527,2475,1022],{"class":573},[527,2477,339],{"class":537},[527,2479,632],{"class":573},[527,2481,2482,2485,2487,2490,2492,2494,2496,2498,2500,2502,2505],{"class":529,"line":1985},[527,2483,2484],{"class":573},"            ()",[527,2486,850],{"class":573},[527,2488,2489],{"class":533}," new",[527,2491,1913],{"class":537},[527,2493,655],{"class":573},[527,2495,2056],{"class":2055},[527,2497,841],{"class":573},[527,2499,2113],{"class":2055},[527,2501,841],{"class":573},[527,2503,2504],{"class":2055}," 0.0",[527,2506,1066],{"class":573},[527,2508,2509,2511,2513,2515,2518,2520,2523,2525,2527,2529,2531],{"class":529,"line":1991},[527,2510,1697],{"class":573},[527,2512,838],{"class":569},[527,2514,841],{"class":573},[527,2516,2517],{"class":569}," event",[527,2519,841],{"class":573},[527,2521,2522],{"class":569}," agg",[527,2524,847],{"class":573},[527,2526,850],{"class":573},[527,2528,2489],{"class":533},[527,2530,1913],{"class":537},[527,2532,632],{"class":573},[527,2534,2535,2538,2540,2543,2546,2549,2551,2554,2556,2559,2561,2564,2567,2570,2572],{"class":529,"line":2000},[527,2536,2537],{"class":761},"                agg",[527,2539,430],{"class":573},[527,2541,2542],{"class":537},"productsViewed",[527,2544,2545],{"class":573},"()",[527,2547,2548],{"class":757}," +",[527,2550,348],{"class":573},[527,2552,2553],{"class":761},"event",[527,2555,430],{"class":573},[527,2557,2558],{"class":537},"isView",[527,2560,2545],{"class":573},[527,2562,2563],{"class":533}," ?",[527,2565,2566],{"class":2055}," 1",[527,2568,2569],{"class":533}," :",[527,2571,2113],{"class":2055},[527,2573,1066],{"class":573},[527,2575,2576,2578,2580,2583,2585,2587,2589,2591,2593,2596,2598,2600,2602,2604,2606],{"class":529,"line":2011},[527,2577,2537],{"class":761},[527,2579,430],{"class":573},[527,2581,2582],{"class":537},"purchases",[527,2584,2545],{"class":573},[527,2586,2548],{"class":757},[527,2588,348],{"class":573},[527,2590,2553],{"class":761},[527,2592,430],{"class":573},[527,2594,2595],{"class":537},"isPurchase",[527,2597,2545],{"class":573},[527,2599,2563],{"class":533},[527,2601,2566],{"class":2055},[527,2603,2569],{"class":533},[527,2605,2113],{"class":2055},[527,2607,1066],{"class":573},[527,2609,2610,2612,2614,2617,2619,2621,2623,2625,2628],{"class":529,"line":2031},[527,2611,2537],{"class":761},[527,2613,430],{"class":573},[527,2615,2616],{"class":537},"amountSpent",[527,2618,2545],{"class":573},[527,2620,2548],{"class":757},[527,2622,2517],{"class":761},[527,2624,430],{"class":573},[527,2626,2627],{"class":537},"amount",[527,2629,2630],{"class":573},"()),\n",[527,2632,2633,2635,2637,2639,2641,2643,2646,2648],{"class":529,"line":2036},[527,2634,1311],{"class":761},[527,2636,430],{"class":573},[527,2638,1287],{"class":537},[527,2640,655],{"class":573},[527,2642,875],{"class":874},[527,2644,2645],{"class":658},"daily-aggregation",[527,2647,875],{"class":874},[527,2649,1066],{"class":573},[527,2651,2652,2654,2656,2658,2660,2662,2664,2666,2668,2670,2672,2674,2676,2679,2681],{"class":529,"line":2045},[527,2653,1330],{"class":569},[527,2655,430],{"class":573},[527,2657,1001],{"class":757},[527,2659,591],{"class":569},[527,2661,841],{"class":573},[527,2663,1913],{"class":569},[527,2665,841],{"class":573},[527,2667,1345],{"class":569},[527,2669,1246],{"class":757},[527,2671,1287],{"class":537},[527,2673,655],{"class":573},[527,2675,875],{"class":874},[527,2677,2678],{"class":658},"daily-aggregation-store",[527,2680,875],{"class":874},[527,2682,1956],{"class":573},[527,2684,2685,2687,2689,2691,2694],{"class":529,"line":2071},[527,2686,2215],{"class":573},[527,2688,2218],{"class":537},[527,2690,655],{"class":573},[527,2692,2693],{"class":569},"dailyAggregatesSerde",[527,2695,896],{"class":573},[898,2697,2699],{"color":2698,"icon":80},"warning",[320,2700,2701,2702,2705,2706,2709],{},"The aggregator must be a ",[324,2703,2704],{},"pure transformation"," of ",[329,2707,2708],{},"(key, value, aggregate)",". Don't mutate the incoming aggregate in place and return it — build and return a new value (the Kotlin example uses a copy; the Java example a new record). The runtime treats the returned object as the new state; mutating shared state defeats the changelog and recovery semantics.",[320,2711,1775,2712,2714,2715,2718,2719,2722,2723,2726,2727,2729,2730,2732],{},[329,2713,339],{}," also offers a config-first overload — ",[329,2716,2717],{},"aggregate(named, materialized, initializerFn = { ... }) { k, v, agg -> ... }",". Only the ",[324,2720,2721],{},"final"," parameter (the aggregator) becomes a trailing lambda block; the initializer is still passed as a regular argument (named here as ",[329,2724,2725],{},"initializerFn","). The Java-friendly overloads take ",[329,2728,1823],{}," and ",[329,2731,1834],{}," explicitly.",[463,2734,2736],{"id":2735},"aggregating-a-table-kgroupedtable","Aggregating a table: KGroupedTable",[320,2738,2739,2742,2743,2746,2747,2749,2750,2753,2754,2758,2759,2762,2763,2765,2766,2769],{},[329,2740,2741],{},"KTable.groupBy(selector)"," produces a ",[329,2744,2745],{},"KGroupedTable\u003CKR, VR>",", not a ",[329,2748,359],{},". The difference is ",[324,2751,2752],{},"changelog semantics",": a table is a stream of updates, so when a record's grouping key changes (a customer moves department, a product is re-categorised), the aggregate for the ",[2755,2756,2757],"em",{},"old"," group must be undone and the aggregate for the ",[2755,2760,2761],{},"new"," group applied. That's why every ",[329,2764,368],{}," fold takes a ",[324,2767,2768],{},"subtractor"," alongside the adder.",[898,2771,2772],{"color":900,"icon":901},[320,2773,2774,2775,2777,2778,2781,2782,2784,2785,2787,2788,2791,2792,2795],{},"The source ",[329,2776,363],{}," must be ",[324,2779,2780],{},"materialized"," (have a state store) for ",[329,2783,355],{}," — the engine reads the old value to know what to subtract. ",[329,2786,456],{}," will throw at build time otherwise. The selector returns a ",[329,2789,2790],{},"KeyValue\u003CKR, VR>",", so it can change both the grouping key and the value. See ",[382,2793,2794],{"href":116},"KStream & KTable"," for materialising a table.",[2797,2798,2800],"h3",{"id":2799},"count-table","count (table)",[320,2802,2803,2805,2806,2808,2809,2811],{},[329,2804,413],{}," on a ",[329,2807,368],{}," returns ",[329,2810,416],{}," and applies the three changelog rules automatically: insert increments, a key change decrements the old key and increments the new, a delete (tombstone) decrements. When a count reaches zero the key is removed from the store.",[513,2813,2814,2886],{"group":515},[517,2815,2817],{"className":519,"code":2816,"filename":521,"language":522,"meta":523,"style":523},"import io.stoatflow.core.state.KeyValue\n\n\u002F\u002F Count users per department, correct under department changes\nval usersPerDept: KTable\u003CString, Long> =\n    usersTable\n        .groupBy { _, user -> KeyValue(user.department, user) }\n        .count()\n",[329,2818,2819,2826,2830,2835,2856,2861,2878],{"__ignoreMap":523},[527,2820,2821,2823],{"class":529,"line":530},[527,2822,534],{"class":533},[527,2824,2825],{"class":537}," io.stoatflow.core.state.KeyValue\n",[527,2827,2828],{"class":529,"line":541},[527,2829,553],{"emptyLinePlaceholder":552},[527,2831,2832],{"class":529,"line":549},[527,2833,2834],{"class":559},"\u002F\u002F Count users per department, correct under department changes\n",[527,2836,2837,2839,2842,2844,2846,2848,2850,2852,2854],{"class":529,"line":556},[527,2838,566],{"class":533},[527,2840,2841],{"class":569}," usersPerDept: ",[527,2843,363],{"class":998},[527,2845,1001],{"class":569},[527,2847,591],{"class":998},[527,2849,332],{"class":569},[527,2851,938],{"class":998},[527,2853,1010],{"class":569},[527,2855,621],{"class":573},[527,2857,2858],{"class":529,"line":563},[527,2859,2860],{"class":569},"    usersTable\n",[527,2862,2863,2865,2867,2870,2872,2875],{"class":529,"line":602},[527,2864,1022],{"class":569},[527,2866,355],{"class":537},[527,2868,2869],{"class":569}," { _, user ",[527,2871,641],{"class":573},[527,2873,2874],{"class":537}," KeyValue",[527,2876,2877],{"class":569},"(user.department, user) }\n",[527,2879,2880,2882,2884],{"class":529,"line":607},[527,2881,1022],{"class":569},[527,2883,331],{"class":537},[527,2885,1477],{"class":569},[517,2887,2889],{"className":676,"code":2888,"filename":678,"language":679,"meta":523,"style":523},"import io.stoatflow.core.state.KeyValue;\nimport io.stoatflow.core.topology.KTable;\n\n\u002F\u002F Count users per department, correct under department changes\nKTable\u003CString, Long> usersPerDept =\n    usersTable\n        .groupBy((key, user) -> KeyValue.pair(user.department(), user))\n        .count();\n",[329,2890,2891,2916,2940,2944,2948,2967,2971,3013],{"__ignoreMap":523},[527,2892,2893,2895,2897,2899,2901,2903,2905,2907,2909,2911,2914],{"class":529,"line":530},[527,2894,534],{"class":533},[527,2896,688],{"class":569},[527,2898,430],{"class":573},[527,2900,693],{"class":569},[527,2902,430],{"class":573},[527,2904,698],{"class":569},[527,2906,430],{"class":573},[527,2908,1123],{"class":569},[527,2910,430],{"class":573},[527,2912,2913],{"class":569},"KeyValue",[527,2915,710],{"class":573},[527,2917,2918,2920,2922,2924,2926,2928,2930,2932,2934,2936,2938],{"class":529,"line":541},[527,2919,534],{"class":533},[527,2921,688],{"class":569},[527,2923,430],{"class":573},[527,2925,693],{"class":569},[527,2927,430],{"class":573},[527,2929,698],{"class":569},[527,2931,430],{"class":573},[527,2933,703],{"class":569},[527,2935,430],{"class":573},[527,2937,363],{"class":569},[527,2939,710],{"class":573},[527,2941,2942],{"class":529,"line":549},[527,2943,553],{"emptyLinePlaceholder":552},[527,2945,2946],{"class":529,"line":556},[527,2947,2834],{"class":559},[527,2949,2950,2952,2954,2956,2958,2960,2962,2965],{"class":529,"line":563},[527,2951,363],{"class":569},[527,2953,1001],{"class":573},[527,2955,591],{"class":757},[527,2957,841],{"class":573},[527,2959,1243],{"class":757},[527,2961,1246],{"class":573},[527,2963,2964],{"class":761}," usersPerDept",[527,2966,819],{"class":573},[527,2968,2969],{"class":529,"line":602},[527,2970,2860],{"class":569},[527,2972,2973,2975,2977,2979,2981,2983,2986,2988,2990,2992,2994,2997,2999,3002,3004,3007,3009,3011],{"class":529,"line":607},[527,2974,1022],{"class":573},[527,2976,355],{"class":537},[527,2978,1264],{"class":573},[527,2980,838],{"class":569},[527,2982,841],{"class":573},[527,2984,2985],{"class":569}," user",[527,2987,847],{"class":573},[527,2989,850],{"class":573},[527,2991,2874],{"class":761},[527,2993,430],{"class":573},[527,2995,2996],{"class":537},"pair",[527,2998,655],{"class":573},[527,3000,3001],{"class":761},"user",[527,3003,430],{"class":573},[527,3005,3006],{"class":537},"department",[527,3008,791],{"class":573},[527,3010,2985],{"class":569},[527,3012,1043],{"class":573},[527,3014,3015,3017,3019],{"class":529,"line":613},[527,3016,1022],{"class":573},[527,3018,331],{"class":537},[527,3020,3021],{"class":573},"();\n",[2797,3023,3025],{"id":3024},"reduce-table","reduce (table)",[320,3027,3028,3031,3032,1390,3034,3036],{},[329,3029,3030],{},"reduce(adder, subtractor)"," folds same-typed values. Both are ",[329,3033,1389],{},[329,3035,1393],{},". On each update the subtractor removes the old value first, then the adder applies the new one.",[513,3038,3039,3139],{"group":515},[517,3040,3042],{"className":519,"code":3041,"filename":521,"language":522,"meta":523,"style":523},"\u002F\u002F Sum scores by department\nval scoreByDept: KTable\u003CString, Int> =\n    usersTable\n        .groupBy { _, user -> KeyValue(user.department, user.score) }\n        .reduce(\n            adder = { agg, score -> agg + score },\n            subtractor = { agg, score -> agg - score },\n        )\n",[329,3043,3044,3049,3070,3074,3089,3097,3117,3135],{"__ignoreMap":523},[527,3045,3046],{"class":529,"line":530},[527,3047,3048],{"class":559},"\u002F\u002F Sum scores by department\n",[527,3050,3051,3053,3056,3058,3060,3062,3064,3066,3068],{"class":529,"line":541},[527,3052,566],{"class":533},[527,3054,3055],{"class":569}," scoreByDept: ",[527,3057,363],{"class":998},[527,3059,1001],{"class":569},[527,3061,591],{"class":998},[527,3063,332],{"class":569},[527,3065,1926],{"class":998},[527,3067,1010],{"class":569},[527,3069,621],{"class":573},[527,3071,3072],{"class":529,"line":549},[527,3073,2860],{"class":569},[527,3075,3076,3078,3080,3082,3084,3086],{"class":529,"line":556},[527,3077,1022],{"class":569},[527,3079,355],{"class":537},[527,3081,2869],{"class":569},[527,3083,641],{"class":573},[527,3085,2874],{"class":537},[527,3087,3088],{"class":569},"(user.department, user.score) }\n",[527,3090,3091,3093,3095],{"class":529,"line":563},[527,3092,1022],{"class":569},[527,3094,335],{"class":537},[527,3096,632],{"class":569},[527,3098,3099,3102,3104,3107,3109,3112,3114],{"class":529,"line":602},[527,3100,3101],{"class":569},"            adder ",[527,3103,574],{"class":573},[527,3105,3106],{"class":569}," { agg, score ",[527,3108,641],{"class":573},[527,3110,3111],{"class":569}," agg ",[527,3113,1498],{"class":757},[527,3115,3116],{"class":569}," score },\n",[527,3118,3119,3122,3124,3126,3128,3130,3133],{"class":529,"line":607},[527,3120,3121],{"class":569},"            subtractor ",[527,3123,574],{"class":573},[527,3125,3106],{"class":569},[527,3127,641],{"class":573},[527,3129,3111],{"class":569},[527,3131,3132],{"class":757},"-",[527,3134,3116],{"class":569},[527,3136,3137],{"class":529,"line":613},[527,3138,1099],{"class":569},[517,3140,3142],{"className":676,"code":3141,"filename":678,"language":679,"meta":523,"style":523},"import io.stoatflow.core.topology.Reducer;\n\n\u002F\u002F Sum scores by department\nReducer\u003CInteger> adder = (agg, score) -> agg + score;\nReducer\u003CInteger> subtractor = (agg, score) -> agg - score;\n\nKTable\u003CString, Integer> scoreByDept =\n    usersTable\n        .groupBy((key, user) -> KeyValue.pair(user.department(), user.score()))\n        .reduce(adder, subtractor);\n",[329,3143,3144,3169,3173,3177,3215,3250,3254,3274,3278,3321],{"__ignoreMap":523},[527,3145,3146,3148,3150,3152,3154,3156,3158,3160,3162,3164,3167],{"class":529,"line":530},[527,3147,534],{"class":533},[527,3149,688],{"class":569},[527,3151,430],{"class":573},[527,3153,693],{"class":569},[527,3155,430],{"class":573},[527,3157,698],{"class":569},[527,3159,430],{"class":573},[527,3161,703],{"class":569},[527,3163,430],{"class":573},[527,3165,3166],{"class":569},"Reducer",[527,3168,710],{"class":573},[527,3170,3171],{"class":529,"line":541},[527,3172,553],{"emptyLinePlaceholder":552},[527,3174,3175],{"class":529,"line":549},[527,3176,3048],{"class":559},[527,3178,3179,3181,3183,3186,3188,3191,3193,3195,3198,3200,3203,3205,3207,3209,3211,3213],{"class":529,"line":556},[527,3180,3166],{"class":569},[527,3182,1001],{"class":573},[527,3184,3185],{"class":757},"Integer",[527,3187,1246],{"class":573},[527,3189,3190],{"class":761}," adder",[527,3192,765],{"class":573},[527,3194,348],{"class":573},[527,3196,3197],{"class":569},"agg",[527,3199,841],{"class":573},[527,3201,3202],{"class":569}," score",[527,3204,847],{"class":573},[527,3206,850],{"class":573},[527,3208,3111],{"class":569},[527,3210,1498],{"class":757},[527,3212,3202],{"class":569},[527,3214,710],{"class":573},[527,3216,3217,3219,3221,3223,3225,3228,3230,3232,3234,3236,3238,3240,3242,3244,3246,3248],{"class":529,"line":563},[527,3218,3166],{"class":569},[527,3220,1001],{"class":573},[527,3222,3185],{"class":757},[527,3224,1246],{"class":573},[527,3226,3227],{"class":761}," subtractor",[527,3229,765],{"class":573},[527,3231,348],{"class":573},[527,3233,3197],{"class":569},[527,3235,841],{"class":573},[527,3237,3202],{"class":569},[527,3239,847],{"class":573},[527,3241,850],{"class":573},[527,3243,3111],{"class":569},[527,3245,3132],{"class":757},[527,3247,3202],{"class":569},[527,3249,710],{"class":573},[527,3251,3252],{"class":529,"line":602},[527,3253,553],{"emptyLinePlaceholder":552},[527,3255,3256,3258,3260,3262,3264,3267,3269,3272],{"class":529,"line":607},[527,3257,363],{"class":569},[527,3259,1001],{"class":573},[527,3261,591],{"class":757},[527,3263,841],{"class":573},[527,3265,3266],{"class":757}," Integer",[527,3268,1246],{"class":573},[527,3270,3271],{"class":761}," scoreByDept",[527,3273,819],{"class":573},[527,3275,3276],{"class":529,"line":613},[527,3277,2860],{"class":569},[527,3279,3280,3282,3284,3286,3288,3290,3292,3294,3296,3298,3300,3302,3304,3306,3308,3310,3312,3314,3316,3319],{"class":529,"line":624},[527,3281,1022],{"class":573},[527,3283,355],{"class":537},[527,3285,1264],{"class":573},[527,3287,838],{"class":569},[527,3289,841],{"class":573},[527,3291,2985],{"class":569},[527,3293,847],{"class":573},[527,3295,850],{"class":573},[527,3297,2874],{"class":761},[527,3299,430],{"class":573},[527,3301,2996],{"class":537},[527,3303,655],{"class":573},[527,3305,3001],{"class":761},[527,3307,430],{"class":573},[527,3309,3006],{"class":537},[527,3311,791],{"class":573},[527,3313,2985],{"class":761},[527,3315,430],{"class":573},[527,3317,3318],{"class":537},"score",[527,3320,599],{"class":573},[527,3322,3323,3325,3327,3329,3332,3334,3336],{"class":529,"line":635},[527,3324,1022],{"class":573},[527,3326,335],{"class":537},[527,3328,655],{"class":573},[527,3330,3331],{"class":569},"adder",[527,3333,841],{"class":573},[527,3335,3227],{"class":569},[527,3337,3338],{"class":573},");\n",[2797,3340,3342],{"id":3341},"aggregate-table","aggregate (table)",[320,3344,3345,3348,3349,397,3352,1390,3354,3356,3357,3360,3361,3364],{},[329,3346,3347],{},"aggregate(initializer, adder, subtractor)"," is the general table fold. The adder and subtractor are ",[324,3350,3351],{},"both",[329,3353,1834],{},[329,3355,1837],{}," (Kafka Streams uses ",[329,3358,3359],{},"Aggregator"," for both roles; there is no separate ",[329,3362,3363],{},"Subtractor"," type). Subtractor runs first on an update, then adder.",[513,3366,3367,3510],{"group":515},[517,3368,3370],{"className":519,"code":3369,"filename":521,"language":522,"meta":523,"style":523},"\u002F\u002F Track the set of user IDs per department\nval membersByDept: KTable\u003CString, Set\u003CString>> =\n    usersTable\n        .groupBy { _, user -> KeyValue(user.department, user) }\n        .aggregate(\n            initializer = { mutableSetOf\u003CString>() },\n            adder = { _, user, set -> set.also { it.add(user.id) } },\n            subtractor = { _, user, set -> set.also { it.remove(user.id) } },\n        )\n",[329,3371,3372,3377,3404,3408,3422,3430,3450,3481,3506],{"__ignoreMap":523},[527,3373,3374],{"class":529,"line":530},[527,3375,3376],{"class":559},"\u002F\u002F Track the set of user IDs per department\n",[527,3378,3379,3381,3384,3386,3388,3390,3392,3395,3397,3399,3402],{"class":529,"line":541},[527,3380,566],{"class":533},[527,3382,3383],{"class":569}," membersByDept: ",[527,3385,363],{"class":998},[527,3387,1001],{"class":569},[527,3389,591],{"class":998},[527,3391,332],{"class":569},[527,3393,3394],{"class":998},"Set",[527,3396,1001],{"class":569},[527,3398,591],{"class":998},[527,3400,3401],{"class":569},">> ",[527,3403,621],{"class":573},[527,3405,3406],{"class":529,"line":549},[527,3407,2860],{"class":569},[527,3409,3410,3412,3414,3416,3418,3420],{"class":529,"line":556},[527,3411,1022],{"class":569},[527,3413,355],{"class":537},[527,3415,2869],{"class":569},[527,3417,641],{"class":573},[527,3419,2874],{"class":537},[527,3421,2877],{"class":569},[527,3423,3424,3426,3428],{"class":529,"line":563},[527,3425,1022],{"class":569},[527,3427,339],{"class":537},[527,3429,632],{"class":569},[527,3431,3432,3435,3437,3440,3443,3445,3447],{"class":529,"line":602},[527,3433,3434],{"class":569},"            initializer ",[527,3436,574],{"class":573},[527,3438,3439],{"class":569}," { ",[527,3441,3442],{"class":537},"mutableSetOf",[527,3444,1001],{"class":569},[527,3446,591],{"class":998},[527,3448,3449],{"class":569},">() },\n",[527,3451,3452,3454,3456,3459,3462,3464,3467,3469,3472,3475,3478],{"class":529,"line":607},[527,3453,3101],{"class":569},[527,3455,574],{"class":573},[527,3457,3458],{"class":569}," { _, user, ",[527,3460,3461],{"class":757},"set",[527,3463,850],{"class":573},[527,3465,3466],{"class":757}," set",[527,3468,430],{"class":569},[527,3470,3471],{"class":537},"also",[527,3473,3474],{"class":569}," { it.",[527,3476,3477],{"class":537},"add",[527,3479,3480],{"class":569},"(user.id) } },\n",[527,3482,3483,3485,3487,3489,3491,3493,3495,3497,3499,3501,3504],{"class":529,"line":613},[527,3484,3121],{"class":569},[527,3486,574],{"class":573},[527,3488,3458],{"class":569},[527,3490,3461],{"class":757},[527,3492,850],{"class":573},[527,3494,3466],{"class":757},[527,3496,430],{"class":569},[527,3498,3471],{"class":537},[527,3500,3474],{"class":569},[527,3502,3503],{"class":537},"remove",[527,3505,3480],{"class":569},[527,3507,3508],{"class":529,"line":624},[527,3509,1099],{"class":569},[517,3511,3513],{"className":676,"code":3512,"filename":678,"language":679,"meta":523,"style":523},"import io.stoatflow.core.topology.Aggregator;\nimport io.stoatflow.core.topology.Initializer;\n\nimport java.util.HashSet;\nimport java.util.Set;\n\n\u002F\u002F Track the set of user IDs per department\nInitializer\u003CSet\u003CString>> initializer = HashSet::new;\nAggregator\u003CString, User, Set\u003CString>> adder = (dept, user, set) -> {\n    set.add(user.id());\n    return set;\n};\nAggregator\u003CString, User, Set\u003CString>> subtractor = (dept, user, set) -> {\n    set.remove(user.id());\n    return set;\n};\n\nKTable\u003CString, Set\u003CString>> membersByDept =\n    usersTable\n        .groupBy((key, user) -> KeyValue.pair(user.department(), user))\n        .aggregate(initializer, adder, subtractor);\n",[329,3514,3515,3539,3564,3568,3587,3603,3607,3611,3639,3687,3708,3717,3722,3766,3784,3792,3796,3800,3823,3827,3865],{"__ignoreMap":523},[527,3516,3517,3519,3521,3523,3525,3527,3529,3531,3533,3535,3537],{"class":529,"line":530},[527,3518,534],{"class":533},[527,3520,688],{"class":569},[527,3522,430],{"class":573},[527,3524,693],{"class":569},[527,3526,430],{"class":573},[527,3528,698],{"class":569},[527,3530,430],{"class":573},[527,3532,703],{"class":569},[527,3534,430],{"class":573},[527,3536,3359],{"class":569},[527,3538,710],{"class":573},[527,3540,3541,3543,3545,3547,3549,3551,3553,3555,3557,3559,3562],{"class":529,"line":541},[527,3542,534],{"class":533},[527,3544,688],{"class":569},[527,3546,430],{"class":573},[527,3548,693],{"class":569},[527,3550,430],{"class":573},[527,3552,698],{"class":569},[527,3554,430],{"class":573},[527,3556,703],{"class":569},[527,3558,430],{"class":573},[527,3560,3561],{"class":569},"Initializer",[527,3563,710],{"class":573},[527,3565,3566],{"class":529,"line":549},[527,3567,553],{"emptyLinePlaceholder":552},[527,3569,3570,3572,3575,3577,3580,3582,3585],{"class":529,"line":556},[527,3571,534],{"class":533},[527,3573,3574],{"class":569}," java",[527,3576,430],{"class":573},[527,3578,3579],{"class":569},"util",[527,3581,430],{"class":573},[527,3583,3584],{"class":569},"HashSet",[527,3586,710],{"class":573},[527,3588,3589,3591,3593,3595,3597,3599,3601],{"class":529,"line":563},[527,3590,534],{"class":533},[527,3592,3574],{"class":569},[527,3594,430],{"class":573},[527,3596,3579],{"class":569},[527,3598,430],{"class":573},[527,3600,3394],{"class":569},[527,3602,710],{"class":573},[527,3604,3605],{"class":529,"line":602},[527,3606,553],{"emptyLinePlaceholder":552},[527,3608,3609],{"class":529,"line":607},[527,3610,3376],{"class":559},[527,3612,3613,3615,3617,3619,3621,3623,3626,3629,3631,3634,3637],{"class":529,"line":613},[527,3614,3561],{"class":569},[527,3616,1001],{"class":573},[527,3618,3394],{"class":569},[527,3620,1001],{"class":573},[527,3622,591],{"class":757},[527,3624,3625],{"class":573},">>",[527,3627,3628],{"class":761}," initializer",[527,3630,765],{"class":573},[527,3632,3633],{"class":569}," HashSet",[527,3635,3636],{"class":533},"::new",[527,3638,710],{"class":573},[527,3640,3641,3643,3645,3647,3649,3652,3654,3657,3659,3661,3663,3665,3667,3669,3672,3674,3676,3678,3680,3682,3684],{"class":529,"line":624},[527,3642,3359],{"class":569},[527,3644,1001],{"class":573},[527,3646,591],{"class":757},[527,3648,841],{"class":573},[527,3650,3651],{"class":757}," User",[527,3653,841],{"class":573},[527,3655,3656],{"class":569}," Set",[527,3658,1001],{"class":573},[527,3660,591],{"class":757},[527,3662,3625],{"class":573},[527,3664,3190],{"class":761},[527,3666,765],{"class":573},[527,3668,348],{"class":573},[527,3670,3671],{"class":569},"dept",[527,3673,841],{"class":573},[527,3675,2985],{"class":569},[527,3677,841],{"class":573},[527,3679,3466],{"class":569},[527,3681,847],{"class":573},[527,3683,850],{"class":573},[527,3685,3686],{"class":573}," {\n",[527,3688,3689,3692,3694,3696,3698,3700,3702,3705],{"class":529,"line":635},[527,3690,3691],{"class":761},"    set",[527,3693,430],{"class":573},[527,3695,3477],{"class":537},[527,3697,655],{"class":573},[527,3699,3001],{"class":761},[527,3701,430],{"class":573},[527,3703,3704],{"class":537},"id",[527,3706,3707],{"class":573},"());\n",[527,3709,3710,3713,3715],{"class":529,"line":647},[527,3711,3712],{"class":533},"    return",[527,3714,3466],{"class":569},[527,3716,710],{"class":573},[527,3718,3719],{"class":529,"line":670},[527,3720,3721],{"class":573},"};\n",[527,3723,3724,3726,3728,3730,3732,3734,3736,3738,3740,3742,3744,3746,3748,3750,3752,3754,3756,3758,3760,3762,3764],{"class":529,"line":1542},[527,3725,3359],{"class":569},[527,3727,1001],{"class":573},[527,3729,591],{"class":757},[527,3731,841],{"class":573},[527,3733,3651],{"class":757},[527,3735,841],{"class":573},[527,3737,3656],{"class":569},[527,3739,1001],{"class":573},[527,3741,591],{"class":757},[527,3743,3625],{"class":573},[527,3745,3227],{"class":761},[527,3747,765],{"class":573},[527,3749,348],{"class":573},[527,3751,3671],{"class":569},[527,3753,841],{"class":573},[527,3755,2985],{"class":569},[527,3757,841],{"class":573},[527,3759,3466],{"class":569},[527,3761,847],{"class":573},[527,3763,850],{"class":573},[527,3765,3686],{"class":573},[527,3767,3768,3770,3772,3774,3776,3778,3780,3782],{"class":529,"line":1985},[527,3769,3691],{"class":761},[527,3771,430],{"class":573},[527,3773,3503],{"class":537},[527,3775,655],{"class":573},[527,3777,3001],{"class":761},[527,3779,430],{"class":573},[527,3781,3704],{"class":537},[527,3783,3707],{"class":573},[527,3785,3786,3788,3790],{"class":529,"line":1991},[527,3787,3712],{"class":533},[527,3789,3466],{"class":569},[527,3791,710],{"class":573},[527,3793,3794],{"class":529,"line":2000},[527,3795,3721],{"class":573},[527,3797,3798],{"class":529,"line":2011},[527,3799,553],{"emptyLinePlaceholder":552},[527,3801,3802,3804,3806,3808,3810,3812,3814,3816,3818,3821],{"class":529,"line":2031},[527,3803,363],{"class":569},[527,3805,1001],{"class":573},[527,3807,591],{"class":757},[527,3809,841],{"class":573},[527,3811,3656],{"class":569},[527,3813,1001],{"class":573},[527,3815,591],{"class":757},[527,3817,3625],{"class":573},[527,3819,3820],{"class":761}," membersByDept",[527,3822,819],{"class":573},[527,3824,3825],{"class":529,"line":2036},[527,3826,2860],{"class":569},[527,3828,3829,3831,3833,3835,3837,3839,3841,3843,3845,3847,3849,3851,3853,3855,3857,3859,3861,3863],{"class":529,"line":2045},[527,3830,1022],{"class":573},[527,3832,355],{"class":537},[527,3834,1264],{"class":573},[527,3836,838],{"class":569},[527,3838,841],{"class":573},[527,3840,2985],{"class":569},[527,3842,847],{"class":573},[527,3844,850],{"class":573},[527,3846,2874],{"class":761},[527,3848,430],{"class":573},[527,3850,2996],{"class":537},[527,3852,655],{"class":573},[527,3854,3001],{"class":761},[527,3856,430],{"class":573},[527,3858,3006],{"class":537},[527,3860,791],{"class":573},[527,3862,2985],{"class":569},[527,3864,1043],{"class":573},[527,3866,3867,3869,3871,3873,3876,3878,3880,3882,3884],{"class":529,"line":2071},[527,3868,1022],{"class":573},[527,3870,339],{"class":537},[527,3872,655],{"class":573},[527,3874,3875],{"class":569},"initializer",[527,3877,841],{"class":573},[527,3879,3190],{"class":569},[527,3881,841],{"class":573},[527,3883,3227],{"class":569},[527,3885,3338],{"class":573},[898,3887,3888],{"color":900,"icon":901},[320,3889,3890,3891,3894,3895,430],{},"When the source table is backed by a ",[324,3892,3893],{},"versioned"," store, out-of-order records (timestamp older than the last seen for that key) are ignored and do not update the aggregate — this prevents late records from corrupting the result. See ",[382,3896,100],{"href":101},[463,3898,3900],{"id":3899},"the-functional-interfaces","The functional interfaces",[320,3902,3903],{},"These are the Java SAM types used across all aggregation methods (Kotlin callers pass lambdas directly). They are import-compatible in name and shape with their Kafka Streams equivalents.",[3905,3906,3907,3923],"table",{},[3908,3909,3910],"thead",{},[3911,3912,3913,3917,3920],"tr",{},[3914,3915,3916],"th",{},"Interface",[3914,3918,3919],{},"Signature",[3914,3921,3922],{},"Used by",[3924,3925,3926,3945,3972],"tbody",{},[3911,3927,3928,3934,3939],{},[3929,3930,3931],"td",{},[329,3932,3933],{},"Initializer\u003CVA>",[3929,3935,3936],{},[329,3937,3938],{},"() -> VA",[3929,3940,3941,3942,3944],{},"every ",[329,3943,339],{}," (initial value)",[3911,3946,3947,3952,3956],{},[3929,3948,3949],{},[329,3950,3951],{},"Aggregator\u003CK, V, VA>",[3929,3953,3954],{},[329,3955,1837],{},[3929,3957,3941,3958,3960,3961,3964,3965,3968,3969,3971],{},[329,3959,339],{}," — adder ",[324,3962,3963],{},"and"," the ",[329,3966,3967],{},"KGroupedTable.aggregate"," subtractor (KS uses ",[329,3970,3359],{}," for both)",[3911,3973,3974,3978,3982],{},[3929,3975,3976],{},[329,3977,1389],{},[3929,3979,3980],{},[329,3981,1393],{},[3929,3983,3941,3984,3986],{},[329,3985,335],{}," (and table adder\u002Fsubtractor)",[320,3988,3989,3990,3993,3994,430],{},"All live in ",[329,3991,3992],{},"io.stoatflow.core.topology",". For the full DSL parity status against Kafka Streams, see the ",[382,3995,301],{"href":302},[463,3997,3999],{"id":3998},"materializing-the-result","Materializing the result",[320,4001,4002,4003,4005],{},"Every aggregation writes to a state store. You control it with ",[329,4004,439],{},":",[389,4007,4008,4016,4026,4033],{},[392,4009,4010,4015],{},[324,4011,4012],{},[329,4013,4014],{},"Materialized.as(\"store-name\")"," — name the store explicitly (recommended; the name is the store's stable identity for changelog topics and recovery).",[392,4017,4018,4023,4024,489],{},[324,4019,4020],{},[329,4021,4022],{},".withValueSerde(serde)"," — required when the aggregate type differs from the input value type (i.e. for ",[329,4025,339],{},[392,4027,4028,4032],{},[324,4029,4030],{},[329,4031,1851],{}," — set both serdes at once.",[392,4034,4035,4040],{},[324,4036,4037],{},[329,4038,4039],{},"Materialized.valueSerde(serde)"," — value serde only, auto-named store.",[320,4042,4043,4044,4046,4047,4049,4050,430],{},"If you omit ",[329,4045,439],{},", the store gets an auto-generated name and falls back to ",[329,4048,443],{}," \u002F the topology default serdes. Stores are durable by default — every aggregation update is committed to a Kafka changelog topic on the commit barrier, so the running result survives restarts. The full store configuration surface (RocksDB vs in-memory, logging, caching, retention) is on ",[382,4051,100],{"href":101},[463,4053,4055],{"id":4054},"consuming-the-result","Consuming the result",[320,4057,4058,352,4060,352,4062,4064,4065,4067,4068,2729,4071,4005],{},[329,4059,331],{},[329,4061,335],{},[329,4063,339],{}," all return a ",[329,4066,363],{},". To emit the running result to a topic, turn it back into a changelog stream with ",[329,4069,4070],{},"toStream()",[329,4072,4073],{},"to(...)",[513,4075,4076,4198],{"group":515},[517,4077,4079],{"className":519,"code":4078,"filename":521,"language":522,"meta":523,"style":523},"import io.stoatflow.core.topology.Named\nimport io.stoatflow.core.topology.Produced\nimport org.apache.kafka.common.serialization.Serdes\n\ncounts\n    .toStream(Named.`as`(\"to-stream\"))\n    .to(\n        \"word-counts\",\n        Produced.`as`\u003CString, Long>(\"sink\")\n            .withKeySerde(Serdes.String())\n            .withValueSerde(Serdes.Long()),\n    )\n",[329,4080,4081,4087,4094,4100,4104,4109,4129,4138,4145,4167,4182,4194],{"__ignoreMap":523},[527,4082,4083,4085],{"class":529,"line":530},[527,4084,534],{"class":533},[527,4086,984],{"class":537},[527,4088,4089,4091],{"class":529,"line":541},[527,4090,534],{"class":533},[527,4092,4093],{"class":537}," io.stoatflow.core.topology.Produced\n",[527,4095,4096,4098],{"class":529,"line":549},[527,4097,534],{"class":533},[527,4099,546],{"class":537},[527,4101,4102],{"class":529,"line":556},[527,4103,553],{"emptyLinePlaceholder":552},[527,4105,4106],{"class":529,"line":563},[527,4107,4108],{"class":569},"counts\n",[527,4110,4111,4114,4117,4120,4122,4124,4127],{"class":529,"line":602},[527,4112,4113],{"class":569},"    .",[527,4115,4116],{"class":537},"toStream",[527,4118,4119],{"class":569},"(Named.",[527,4121,1035],{"class":537},[527,4123,655],{"class":569},[527,4125,4126],{"class":658},"\"to-stream\"",[527,4128,1043],{"class":569},[527,4130,4131,4133,4136],{"class":529,"line":607},[527,4132,4113],{"class":569},[527,4134,4135],{"class":537},"to",[527,4137,632],{"class":569},[527,4139,4140,4143],{"class":529,"line":613},[527,4141,4142],{"class":658},"        \"word-counts\"",[527,4144,1718],{"class":569},[527,4146,4147,4150,4152,4154,4156,4158,4160,4162,4165],{"class":529,"line":624},[527,4148,4149],{"class":569},"        Produced.",[527,4151,1035],{"class":537},[527,4153,1001],{"class":569},[527,4155,591],{"class":998},[527,4157,332],{"class":569},[527,4159,938],{"class":998},[527,4161,1089],{"class":569},[527,4163,4164],{"class":658},"\"sink\"",[527,4166,1956],{"class":569},[527,4168,4169,4172,4175,4177,4179],{"class":529,"line":635},[527,4170,4171],{"class":569},"            .",[527,4173,4174],{"class":537},"withKeySerde",[527,4176,588],{"class":569},[527,4178,591],{"class":537},[527,4180,4181],{"class":569},"())\n",[527,4183,4184,4186,4188,4190,4192],{"class":529,"line":647},[527,4185,4171],{"class":569},[527,4187,2218],{"class":537},[527,4189,588],{"class":569},[527,4191,938],{"class":537},[527,4193,2630],{"class":569},[527,4195,4196],{"class":529,"line":670},[527,4197,673],{"class":569},[517,4199,4201],{"className":676,"code":4200,"filename":678,"language":679,"meta":523,"style":523},"import io.stoatflow.core.topology.Named;\nimport io.stoatflow.core.topology.Produced;\nimport org.apache.kafka.common.serialization.Serdes;\n\ncounts\n    .toStream(Named.as(\"to-stream\"))\n    .to(\n        \"word-counts\",\n        Produced.\u003CString, Long>as(\"sink\")\n            .withKeySerde(Serdes.String())\n            .withValueSerde(Serdes.Long()));\n",[329,4202,4203,4227,4252,4280,4284,4288,4313,4321,4332,4362,4378],{"__ignoreMap":523},[527,4204,4205,4207,4209,4211,4213,4215,4217,4219,4221,4223,4225],{"class":529,"line":530},[527,4206,534],{"class":533},[527,4208,688],{"class":569},[527,4210,430],{"class":573},[527,4212,693],{"class":569},[527,4214,430],{"class":573},[527,4216,698],{"class":569},[527,4218,430],{"class":573},[527,4220,703],{"class":569},[527,4222,430],{"class":573},[527,4224,1224],{"class":569},[527,4226,710],{"class":573},[527,4228,4229,4231,4233,4235,4237,4239,4241,4243,4245,4247,4250],{"class":529,"line":541},[527,4230,534],{"class":533},[527,4232,688],{"class":569},[527,4234,430],{"class":573},[527,4236,693],{"class":569},[527,4238,430],{"class":573},[527,4240,698],{"class":569},[527,4242,430],{"class":573},[527,4244,703],{"class":569},[527,4246,430],{"class":573},[527,4248,4249],{"class":569},"Produced",[527,4251,710],{"class":573},[527,4253,4254,4256,4258,4260,4262,4264,4266,4268,4270,4272,4274,4276,4278],{"class":529,"line":549},[527,4255,534],{"class":533},[527,4257,717],{"class":569},[527,4259,430],{"class":573},[527,4261,722],{"class":569},[527,4263,430],{"class":573},[527,4265,727],{"class":569},[527,4267,430],{"class":573},[527,4269,732],{"class":569},[527,4271,430],{"class":573},[527,4273,737],{"class":569},[527,4275,430],{"class":573},[527,4277,742],{"class":569},[527,4279,710],{"class":573},[527,4281,4282],{"class":529,"line":556},[527,4283,553],{"emptyLinePlaceholder":552},[527,4285,4286],{"class":529,"line":563},[527,4287,4108],{"class":569},[527,4289,4290,4292,4294,4296,4298,4300,4302,4304,4306,4309,4311],{"class":529,"line":602},[527,4291,4113],{"class":573},[527,4293,4116],{"class":537},[527,4295,655],{"class":573},[527,4297,1224],{"class":761},[527,4299,430],{"class":573},[527,4301,1287],{"class":537},[527,4303,655],{"class":573},[527,4305,875],{"class":874},[527,4307,4308],{"class":658},"to-stream",[527,4310,875],{"class":874},[527,4312,1043],{"class":573},[527,4314,4315,4317,4319],{"class":529,"line":607},[527,4316,4113],{"class":573},[527,4318,4135],{"class":537},[527,4320,632],{"class":573},[527,4322,4323,4326,4328,4330],{"class":529,"line":613},[527,4324,4325],{"class":874},"        \"",[527,4327,1356],{"class":658},[527,4329,875],{"class":874},[527,4331,1718],{"class":573},[527,4333,4334,4337,4339,4341,4343,4345,4347,4349,4351,4353,4355,4358,4360],{"class":529,"line":624},[527,4335,4336],{"class":569},"        Produced",[527,4338,430],{"class":573},[527,4340,1001],{"class":757},[527,4342,591],{"class":569},[527,4344,841],{"class":573},[527,4346,1243],{"class":569},[527,4348,1246],{"class":757},[527,4350,1287],{"class":537},[527,4352,655],{"class":573},[527,4354,875],{"class":874},[527,4356,4357],{"class":658},"sink",[527,4359,875],{"class":874},[527,4361,1956],{"class":573},[527,4363,4364,4366,4368,4370,4372,4374,4376],{"class":529,"line":635},[527,4365,4171],{"class":573},[527,4367,4174],{"class":537},[527,4369,655],{"class":573},[527,4371,742],{"class":761},[527,4373,430],{"class":573},[527,4375,591],{"class":537},[527,4377,4181],{"class":573},[527,4379,4380,4382,4384,4386,4388,4390,4392],{"class":529,"line":647},[527,4381,4171],{"class":573},[527,4383,2218],{"class":537},[527,4385,655],{"class":573},[527,4387,742],{"class":761},[527,4389,430],{"class":573},[527,4391,938],{"class":537},[527,4393,801],{"class":573},[320,4395,4396,4397,4399],{},"Because the output is a changelog, downstream consumers see every intermediate update of a key's aggregate, not just a final value — that's the expected ",[329,4398,363],{}," semantics, the same as Kafka Streams.",[463,4401,4403],{"id":4402},"next-steps","Next steps",[389,4405,4406,4415,4425,4432],{},[392,4407,4408,4412,4413,430],{},[324,4409,4410],{},[382,4411,125],{"href":126}," — time, sliding, and session windows on grouped streams, plus ",[329,4414,379],{},[392,4416,4417,4421,4422,4424],{},[324,4418,4419],{},[382,4420,2794],{"href":116}," — materialising a table so ",[329,4423,456],{}," can track old values.",[392,4426,4427,4431],{},[324,4428,4429],{},[382,4430,100],{"href":101}," — store types, changelog, in-memory vs RocksDB, versioned stores.",[392,4433,4434,4439],{},[324,4435,4436],{},[382,4437,4438],{"href":106},"Testing"," — assert on aggregation results with the in-memory test driver.",[4441,4442,4443],"style",{},"html pre.shiki code .sbBg2, html code.shiki .sbBg2{--shiki-default:#1E754F}html pre.shiki code .sySUi, html code.shiki .sySUi{--shiki-default:#59873A}html pre.shiki code .s8zF2, html code.shiki .s8zF2{--shiki-default:#A0ADA0}html pre.shiki code .suHK_, html code.shiki .suHK_{--shiki-default:#393A34}html pre.shiki code .sYZai, html code.shiki .sYZai{--shiki-default:#999999}html pre.shiki code .spphp, html code.shiki .spphp{--shiki-default:#B56959}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .si04Y, html code.shiki .si04Y{--shiki-default:#AB5959}html pre.shiki code .svycV, html code.shiki .svycV{--shiki-default:#B07D48}html pre.shiki code .sSP4y, html code.shiki .sSP4y{--shiki-default:#B5695977}html pre.shiki code .sUxyF, html code.shiki .sUxyF{--shiki-default:#2E8F82}html pre.shiki code .s-TwI, html code.shiki .s-TwI{--shiki-default:#2F798A}",{"title":523,"searchDepth":541,"depth":541,"links":4445},[4446,4447,4448,4449,4450,4455,4456,4457,4458],{"id":465,"depth":541,"text":466},{"id":331,"depth":541,"text":331},{"id":335,"depth":541,"text":335},{"id":339,"depth":541,"text":339},{"id":2735,"depth":541,"text":2736,"children":4451},[4452,4453,4454],{"id":2799,"depth":549,"text":2800},{"id":3024,"depth":549,"text":3025},{"id":3341,"depth":549,"text":3342},{"id":3899,"depth":541,"text":3900},{"id":3998,"depth":541,"text":3999},{"id":4054,"depth":541,"text":4055},{"id":4402,"depth":541,"text":4403},"Group records by key and fold them into running totals — count, reduce, aggregate on grouped streams and grouped tables, with the result backed by a state store.","md",{},{"icon":123},"how-to",{"title":120,"description":4459},"-awwMqpD7A-7QeiBu5k98rQ6P4AsSFBYPfAJYI5M-Ok",[4467,4469],{"title":115,"path":116,"stem":117,"description":4468,"icon":118,"children":-1},"Stateless stream transforms (map, filter, flatMap, selectKey, branch, merge) and KTable basics — with the rules for when a key change forces records onto a different lane.",{"title":125,"path":126,"stem":127,"description":4470,"icon":128,"children":-1},"Windowed aggregations in StoatFlow — tumbling, hopping, sliding, and session windows; emit strategies, suppression, Windowed keys, and WindowedSerdes.",1783713358637]