Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/db-ivm/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ export * from './d2.js'
export * from './multiset.js'
export * from './operators/index.js'
export * from './types.js'
export { compareKeys } from './utils.js'
export { compareKeys, serializeValue } from './utils.js'
3 changes: 2 additions & 1 deletion packages/db-ivm/src/operators/groupBy.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { serializeValue } from '../utils.js'
import { map } from './map.js'
import { reduce } from './reduce.js'
import type { IStreamBuilder, KeyValue } from '../types.js'
Expand Down Expand Up @@ -67,7 +68,7 @@ export function groupBy<
const withKeysAndValues = stream.pipe(
map((data) => {
const key = keyExtractor(data)
const keyString = JSON.stringify(key)
const keyString = serializeValue(key)

// Create values object with pre-aggregated values
const values: Record<string, unknown> = {}
Expand Down
17 changes: 17 additions & 0 deletions packages/db-ivm/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,3 +192,20 @@ export function compareKeys(a: string | number, b: string | number): number {
// Different types: strings come before numbers
return typeof a === `string` ? -1 : 1
}

/**
* Serializes a value for use as a key, handling BigInt and Date values that JSON.stringify cannot handle.
* Uses JSON.stringify with a replacer function to convert BigInt values to strings and Date values to ISO strings.
* This is used for creating string keys in groupBy operations.
*/
export function serializeValue(value: unknown): string {
return JSON.stringify(value, (_, val) => {
if (typeof val === 'bigint') {
return val.toString()
}
if (val instanceof Date) {
return val.toISOString()
}
return val
})
}
Comment on lines +201 to +211
Copy link

Copilot AI Jan 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new serializeValue utility function lacks unit tests in the db-ivm package. While integration tests exist in the db package, consider adding unit tests in packages/db-ivm/tests/utils.test.ts to verify edge cases like null values, undefined values, nested objects with BigInt/Date, arrays containing BigInt/Date, and mixed types. This would improve maintainability and ensure the function behaves correctly in isolation.

Copilot uses AI. Check for mistakes.
10 changes: 8 additions & 2 deletions packages/db/src/query/compiler/group-by.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import { filter, groupBy, groupByOperators, map } from '@tanstack/db-ivm'
import {
filter,
groupBy,
groupByOperators,
map,
serializeValue,
} from '@tanstack/db-ivm'
import { Func, PropRef, getHavingExpression } from '../ir.js'
import {
AggregateFunctionNotInSelectError,
Expand Down Expand Up @@ -248,7 +254,7 @@ export function processGroupBy(
for (let i = 0; i < groupByClause.length; i++) {
keyParts.push(aggregatedRow[`__key_${i}`])
}
finalKey = JSON.stringify(keyParts)
finalKey = serializeValue(keyParts)
}

return [
Expand Down
Loading
Loading