Skip to content
Open
Show file tree
Hide file tree
Changes from 5 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