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
5 changes: 5 additions & 0 deletions .changeset/distinct-getkey-error-message.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@tanstack/db': patch
---

Improve DuplicateKeySyncError message when using `.distinct()` with custom `getKey`. The error now explains that `.distinct()` deduplicates by the entire selected object, and provides actionable guidance to fix the issue.
1 change: 1 addition & 0 deletions packages/db/src/collection/sync.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ export class CollectionSyncManager<
throw new DuplicateKeySyncError(key, this.id, {
hasCustomGetKey: internal?.hasCustomGetKey ?? false,
hasJoins: internal?.hasJoins ?? false,
hasDistinct: internal?.hasDistinct ?? false,
})
}
}
Expand Down
22 changes: 19 additions & 3 deletions packages/db/src/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,12 +172,28 @@ export class DuplicateKeySyncError extends CollectionOperationError {
constructor(
key: string | number,
collectionId: string,
options?: { hasCustomGetKey?: boolean; hasJoins?: boolean },
options?: {
hasCustomGetKey?: boolean
hasJoins?: boolean
hasDistinct?: boolean
},
) {
const baseMessage = `Cannot insert document with key "${key}" from sync because it already exists in the collection "${collectionId}"`

// Provide enhanced guidance when custom getKey is used with joins
if (options?.hasCustomGetKey && options.hasJoins) {
// Provide enhanced guidance when custom getKey is used with distinct
if (options?.hasCustomGetKey && options.hasDistinct) {
super(
`${baseMessage}. ` +
`This collection uses a custom getKey with .distinct(). ` +
`The .distinct() operator deduplicates by the ENTIRE selected object (standard SQL behavior), ` +
`but your custom getKey extracts only a subset of fields. This causes multiple distinct rows ` +
`(with different values in non-key fields) to receive the same key. ` +
`To fix this, either: (1) ensure your SELECT only includes fields that uniquely identify each row, ` +
`(2) use .groupBy() with min()/max() aggregates to select one value per group, or ` +
`(3) remove the custom getKey to use the default key behavior.`,
)
} else if (options?.hasCustomGetKey && options.hasJoins) {
// Provide enhanced guidance when custom getKey is used with joins
super(
`${baseMessage}. ` +
`This collection uses a custom getKey with joined queries. ` +
Expand Down
1 change: 1 addition & 0 deletions packages/db/src/query/live/collection-config-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,7 @@ export class CollectionConfigBuilder<
getBuilder: () => this,
hasCustomGetKey: !!this.config.getKey,
hasJoins: this.hasJoins(this.query),
hasDistinct: !!this.query.distinct,
},
},
}
Expand Down
1 change: 1 addition & 0 deletions packages/db/src/query/live/internal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ export type LiveQueryInternalUtils = {
getBuilder: () => CollectionConfigBuilder<any, any>
hasCustomGetKey: boolean
hasJoins: boolean
hasDistinct: boolean
}
Loading