{"version":3,"file":"group-by.cjs","sources":["../../../src/query/group-by.ts"],"sourcesContent":["import { groupBy, groupByOperators } from \"@electric-sql/d2mini\"\nimport {\n  evaluateOperandOnNamespacedRow,\n  extractValueFromNamespacedRow,\n} from \"./extractors\"\nimport { isAggregateFunctionCall } from \"./utils\"\nimport type { ConditionOperand, FunctionCall, Query } from \"./schema\"\nimport type { NamespacedAndKeyedStream } from \"../types.js\"\n\nconst { sum, count, avg, min, max, median, mode } = groupByOperators\n\n/**\n * Process the groupBy clause in a D2QL query\n */\nexport function processGroupBy(\n  pipeline: NamespacedAndKeyedStream,\n  query: Query,\n  mainTableAlias: string\n) {\n  // Normalize groupBy to an array of column references\n  const groupByColumns = Array.isArray(query.groupBy)\n    ? query.groupBy\n    : [query.groupBy]\n\n  // Create a key extractor function for the groupBy operator\n  const keyExtractor = ([_oldKey, namespacedRow]: [\n    string,\n    Record<string, unknown>,\n  ]) => {\n    const key: Record<string, unknown> = {}\n\n    // Extract each groupBy column value\n    for (const column of groupByColumns) {\n      if (typeof column === `string` && (column as string).startsWith(`@`)) {\n        const columnRef = (column as string).substring(1)\n        const columnName = columnRef.includes(`.`)\n          ? columnRef.split(`.`)[1]\n          : columnRef\n\n        key[columnName!] = extractValueFromNamespacedRow(\n          namespacedRow,\n          columnRef,\n          mainTableAlias\n        )\n      }\n    }\n\n    return key\n  }\n\n  // Create aggregate functions for any aggregated columns in the SELECT clause\n  const aggregates: Record<string, any> = {}\n\n  if (!query.select) {\n    throw new Error(`SELECT clause is required for GROUP BY`)\n  }\n\n  // Scan the SELECT clause for aggregate functions\n  for (const item of query.select) {\n    if (typeof item === `object`) {\n      for (const [alias, expr] of Object.entries(item)) {\n        if (typeof expr === `object` && isAggregateFunctionCall(expr)) {\n          // Get the function name (the only key in the object)\n          const functionName = Object.keys(expr)[0]\n          // Get the column reference or expression to aggregate\n          const columnRef = (expr as FunctionCall)[\n            functionName as keyof FunctionCall\n          ]\n\n          // Add the aggregate function to our aggregates object\n          aggregates[alias] = getAggregateFunction(\n            functionName!,\n            columnRef,\n            mainTableAlias\n          )\n        }\n      }\n    }\n  }\n\n  // Apply the groupBy operator if we have any aggregates\n  if (Object.keys(aggregates).length > 0) {\n    pipeline = pipeline.pipe(groupBy(keyExtractor, aggregates))\n  }\n\n  return pipeline\n}\n\n/**\n * Helper function to get an aggregate function based on the function name\n */\nexport function getAggregateFunction(\n  functionName: string,\n  columnRef: string | ConditionOperand,\n  mainTableAlias: string\n) {\n  // Create a value extractor function for the column to aggregate\n  const valueExtractor = ([_oldKey, namespacedRow]: [\n    string,\n    Record<string, unknown>,\n  ]) => {\n    let value: unknown\n    if (typeof columnRef === `string` && columnRef.startsWith(`@`)) {\n      value = extractValueFromNamespacedRow(\n        namespacedRow,\n        columnRef.substring(1),\n        mainTableAlias\n      )\n    } else {\n      value = evaluateOperandOnNamespacedRow(\n        namespacedRow,\n        columnRef as ConditionOperand,\n        mainTableAlias\n      )\n    }\n    // Ensure we return a number for aggregate functions\n    return typeof value === `number` ? value : 0\n  }\n\n  // Return the appropriate aggregate function\n  switch (functionName.toUpperCase()) {\n    case `SUM`:\n      return sum(valueExtractor)\n    case `COUNT`:\n      return count() // count() doesn't need a value extractor\n    case `AVG`:\n      return avg(valueExtractor)\n    case `MIN`:\n      return min(valueExtractor)\n    case `MAX`:\n      return max(valueExtractor)\n    case `MEDIAN`:\n      return median(valueExtractor)\n    case `MODE`:\n      return mode(valueExtractor)\n    default:\n      throw new Error(`Unsupported aggregate function: ${functionName}`)\n  }\n}\n"],"names":["groupByOperators","extractValueFromNamespacedRow","isAggregateFunctionCall","groupBy","evaluateOperandOnNamespacedRow"],"mappings":";;;;;AASA,MAAM,EAAE,KAAK,OAAO,KAAK,KAAK,KAAK,QAAQ,SAASA,OAAA;AAKpC,SAAA,eACd,UACA,OACA,gBACA;AAEM,QAAA,iBAAiB,MAAM,QAAQ,MAAM,OAAO,IAC9C,MAAM,UACN,CAAC,MAAM,OAAO;AAGlB,QAAM,eAAe,CAAC,CAAC,SAAS,aAAa,MAGvC;AACJ,UAAM,MAA+B,CAAC;AAGtC,eAAW,UAAU,gBAAgB;AACnC,UAAI,OAAO,WAAW,YAAa,OAAkB,WAAW,GAAG,GAAG;AAC9D,cAAA,YAAa,OAAkB,UAAU,CAAC;AAC1C,cAAA,aAAa,UAAU,SAAS,GAAG,IACrC,UAAU,MAAM,GAAG,EAAE,CAAC,IACtB;AAEJ,YAAI,UAAW,IAAIC,WAAA;AAAA,UACjB;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAAA,MAAA;AAAA,IACF;AAGK,WAAA;AAAA,EACT;AAGA,QAAM,aAAkC,CAAC;AAErC,MAAA,CAAC,MAAM,QAAQ;AACX,UAAA,IAAI,MAAM,wCAAwC;AAAA,EAAA;AAI/C,aAAA,QAAQ,MAAM,QAAQ;AAC3B,QAAA,OAAO,SAAS,UAAU;AAC5B,iBAAW,CAAC,OAAO,IAAI,KAAK,OAAO,QAAQ,IAAI,GAAG;AAChD,YAAI,OAAO,SAAS,YAAYC,MAAA,wBAAwB,IAAI,GAAG;AAE7D,gBAAM,eAAe,OAAO,KAAK,IAAI,EAAE,CAAC;AAElC,gBAAA,YAAa,KACjB,YACF;AAGA,qBAAW,KAAK,IAAI;AAAA,YAClB;AAAA,YACA;AAAA,YACA;AAAA,UACF;AAAA,QAAA;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAIF,MAAI,OAAO,KAAK,UAAU,EAAE,SAAS,GAAG;AACtC,eAAW,SAAS,KAAKC,OAAQ,QAAA,cAAc,UAAU,CAAC;AAAA,EAAA;AAGrD,SAAA;AACT;AAKgB,SAAA,qBACd,cACA,WACA,gBACA;AAEA,QAAM,iBAAiB,CAAC,CAAC,SAAS,aAAa,MAGzC;AACA,QAAA;AACJ,QAAI,OAAO,cAAc,YAAY,UAAU,WAAW,GAAG,GAAG;AACtD,cAAAF,WAAA;AAAA,QACN;AAAA,QACA,UAAU,UAAU,CAAC;AAAA,QACrB;AAAA,MACF;AAAA,IAAA,OACK;AACG,cAAAG,WAAA;AAAA,QACN;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IAAA;AAGK,WAAA,OAAO,UAAU,WAAW,QAAQ;AAAA,EAC7C;AAGQ,UAAA,aAAa,YAAe,GAAA;AAAA,IAClC,KAAK;AACH,aAAO,IAAI,cAAc;AAAA,IAC3B,KAAK;AACH,aAAO,MAAM;AAAA;AAAA,IACf,KAAK;AACH,aAAO,IAAI,cAAc;AAAA,IAC3B,KAAK;AACH,aAAO,IAAI,cAAc;AAAA,IAC3B,KAAK;AACH,aAAO,IAAI,cAAc;AAAA,IAC3B,KAAK;AACH,aAAO,OAAO,cAAc;AAAA,IAC9B,KAAK;AACH,aAAO,KAAK,cAAc;AAAA,IAC5B;AACE,YAAM,IAAI,MAAM,mCAAmC,YAAY,EAAE;AAAA,EAAA;AAEvE;;;"}