Skip to content

Commit a3b3865

Browse files
authored
fix: hasMany / polymorphic relationships to custom number IDs (#14201)
Fixes #14197 Ensures that any numeric column is sanitized to a number JS type on Drizzle side Properly converts `BigInt` relationship values if `useBigIntForNumberIDs: true` is passed to the mongodb adapter (e.g `compatibilityOptions.firestore`)
1 parent bcd40b6 commit a3b3865

File tree

9 files changed

+110
-10
lines changed

9 files changed

+110
-10
lines changed

packages/db-mongodb/src/utilities/transform.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ const processRelationshipValues = (
3838
config: SanitizedConfig,
3939
operation: 'read' | 'write',
4040
validateRelationships: boolean,
41+
adapter: MongooseAdapter,
4142
) => {
4243
return items.map((item) => {
4344
// Handle polymorphic relationships
@@ -47,6 +48,7 @@ const processRelationshipValues = (
4748
return {
4849
relationTo: item.relationTo,
4950
value: convertRelationshipValue({
51+
adapter,
5052
operation,
5153
relatedCollection,
5254
validateRelationships,
@@ -62,6 +64,7 @@ const processRelationshipValues = (
6264
const relatedCollection = config.collections?.find(({ slug }) => slug === field.relationTo)
6365
if (relatedCollection) {
6466
return convertRelationshipValue({
67+
adapter,
6568
operation,
6669
relatedCollection,
6770
validateRelationships,
@@ -75,11 +78,13 @@ const processRelationshipValues = (
7578
}
7679

7780
const convertRelationshipValue = ({
81+
adapter,
7882
operation,
7983
relatedCollection,
8084
validateRelationships,
8185
value,
8286
}: {
87+
adapter: MongooseAdapter
8388
operation: Args['operation']
8489
relatedCollection: CollectionConfig
8590
validateRelationships?: boolean
@@ -94,6 +99,14 @@ const convertRelationshipValue = ({
9499
return value.toHexString()
95100
}
96101

102+
if (
103+
customIDField?.type === 'number' &&
104+
typeof value === 'bigint' &&
105+
adapter.useBigIntForNumberIDs
106+
) {
107+
return Number(value)
108+
}
109+
97110
return value
98111
}
99112

@@ -116,6 +129,7 @@ const convertRelationshipValue = ({
116129
}
117130

118131
const sanitizeRelationship = ({
132+
adapter,
119133
config,
120134
field,
121135
locale,
@@ -124,6 +138,7 @@ const sanitizeRelationship = ({
124138
validateRelationships,
125139
value,
126140
}: {
141+
adapter: MongooseAdapter
127142
config: SanitizedConfig
128143
field: JoinField | RelationshipField | UploadField
129144
locale?: string
@@ -175,6 +190,7 @@ const sanitizeRelationship = ({
175190
return {
176191
relationTo: val.relationTo,
177192
value: convertRelationshipValue({
193+
adapter,
178194
operation,
179195
relatedCollection: relatedCollectionForSingleValue,
180196
validateRelationships,
@@ -186,6 +202,7 @@ const sanitizeRelationship = ({
186202

187203
if (relatedCollection) {
188204
return convertRelationshipValue({
205+
adapter,
189206
operation,
190207
relatedCollection,
191208
validateRelationships,
@@ -204,6 +221,7 @@ const sanitizeRelationship = ({
204221
result = {
205222
relationTo: value.relationTo,
206223
value: convertRelationshipValue({
224+
adapter,
207225
operation,
208226
relatedCollection,
209227
validateRelationships,
@@ -215,6 +233,7 @@ const sanitizeRelationship = ({
215233
// Handle has one
216234
else if (relatedCollection) {
217235
result = convertRelationshipValue({
236+
adapter,
218237
operation,
219238
relatedCollection,
220239
validateRelationships,
@@ -657,6 +676,7 @@ export const transform = ({
657676
config,
658677
operation,
659678
validateRelationships,
679+
adapter,
660680
)
661681
$addToSet[`${parentPath}${field.name}.${localeKey}`] = { $each: processedLocaleItems }
662682
}
@@ -675,6 +695,7 @@ export const transform = ({
675695
config,
676696
operation,
677697
validateRelationships,
698+
adapter,
678699
)
679700
$addToSet[`${parentPath}${field.name}`] = { $each: processedItems }
680701
delete ref[field.name]
@@ -721,6 +742,7 @@ export const transform = ({
721742
config,
722743
operation,
723744
validateRelationships,
745+
adapter,
724746
)
725747
$pull[`${parentPath}${field.name}.${localeKey}`] = { $in: processedLocaleItems }
726748
}
@@ -739,6 +761,7 @@ export const transform = ({
739761
config,
740762
operation,
741763
validateRelationships,
764+
adapter,
742765
)
743766
$pull[`${parentPath}${field.name}`] = { $in: processedItems }
744767
delete ref[field.name]
@@ -792,6 +815,7 @@ export const transform = ({
792815
const value = fieldRef[code]
793816
if (value) {
794817
sanitizeRelationship({
818+
adapter,
795819
config,
796820
field,
797821
locale: code,
@@ -805,6 +829,7 @@ export const transform = ({
805829
} else {
806830
// handle non-localized relationships
807831
sanitizeRelationship({
832+
adapter,
808833
config,
809834
field,
810835
locale: undefined,

packages/drizzle/src/postgres/columnToCodeConverter.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ export const columnToCodeConverter: ColumnToCodeConverter = ({
3434
}
3535
break
3636
}
37+
case 'numeric': {
38+
columnBuilderArgsArray.push("mode: 'number'")
39+
break
40+
}
3741
case 'timestamp': {
3842
columnBuilderArgsArray.push(`mode: '${column.mode}'`)
3943
if (column.withTimezone) {

packages/drizzle/src/postgres/schema/buildDrizzleTable.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,11 @@ export const buildDrizzleTable = ({
7474
break
7575
}
7676

77+
case 'numeric': {
78+
columns[key] = numeric(column.name, { mode: 'number' })
79+
break
80+
}
81+
7782
case 'sparsevec': {
7883
const builder = sparsevec(column.name, { dimensions: column.dimensions })
7984

packages/drizzle/src/sqlite/columnToCodeConverter.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,11 @@ export const columnToCodeConverter: ColumnToCodeConverter = ({
4444
break
4545
}
4646

47+
case 'numeric': {
48+
columnBuilderArgsArray.push("mode: 'number'")
49+
break
50+
}
51+
4752
case 'serial': {
4853
columnBuilderFn = 'integer'
4954
break

packages/drizzle/src/sqlite/schema/buildDrizzleTable.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,11 @@ export const buildDrizzleTable: BuildDrizzleTable = ({ adapter, locales, rawTabl
4444
break
4545
}
4646

47+
case 'numeric': {
48+
columns[key] = numeric(column.name, { mode: 'number' })
49+
break
50+
}
51+
4752
case 'serial': {
4853
columns[key] = integer(column.name)
4954
break

test/database/getConfig.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,12 @@ export const getConfig: () => Partial<Config> = () => ({
147147
hasMany: true,
148148
name: 'categories',
149149
},
150+
{
151+
type: 'relationship',
152+
relationTo: 'categories-custom-id',
153+
hasMany: true,
154+
name: 'categoriesCustomID',
155+
},
150156
{
151157
type: 'relationship',
152158
relationTo: ['categories'],

test/database/int.spec.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,18 @@ describe('database', () => {
212212
expect(duplicate.arrayWithIDs[0].id).not.toStrictEqual(arrayRowID)
213213
expect(duplicate.blocksWithIDs[0].id).not.toStrictEqual(blockID)
214214
})
215+
216+
it('should properly give the result with hasMany relationships with custom numeric IDs', async () => {
217+
await payload.create({ collection: 'categories-custom-id', data: { id: 9999 } })
218+
const res = await payload.create({
219+
collection: 'posts',
220+
data: { title: 'post', categoriesCustomID: [9999] },
221+
depth: 0,
222+
})
223+
expect(res.categoriesCustomID[0]).toBe(9999)
224+
const resFind = await payload.findByID({ collection: 'posts', id: res.id, depth: 0 })
225+
expect(resFind.categoriesCustomID[0]).toBe(9999)
226+
})
215227
})
216228

217229
describe('timestamps', () => {

0 commit comments

Comments
 (0)