chore: builds hasMany selects
This commit is contained in:
@@ -12,7 +12,7 @@ import { buildTable } from './schema/build'
|
||||
|
||||
export const init: Init = async function init(this: PostgresAdapter) {
|
||||
if (this.payload.config.localization) {
|
||||
this.enums._locales = pgEnum(
|
||||
this.enums.enum__locales = pgEnum(
|
||||
'_locales',
|
||||
// TODO: types out of sync with core, monorepo please
|
||||
// this.payload.config.localization.localeCodes,
|
||||
|
||||
@@ -33,7 +33,7 @@ type Args = {
|
||||
}
|
||||
|
||||
type Result = {
|
||||
arrayBlockRelations: Map<string, string>
|
||||
relationsToBuild: Map<string, string>
|
||||
}
|
||||
|
||||
export const buildTable = ({
|
||||
@@ -61,7 +61,7 @@ export const buildTable = ({
|
||||
const relationships: Set<string> = new Set()
|
||||
let relationshipsTable: GenericTable
|
||||
|
||||
const arrayBlockRelations: Map<string, string> = new Map()
|
||||
const relationsToBuild: Map<string, string> = new Map()
|
||||
|
||||
const idField = fields.find((field) => fieldAffectsData(field) && field.name === 'id')
|
||||
let idColType = 'integer'
|
||||
@@ -87,7 +87,6 @@ export const buildTable = ({
|
||||
hasManyNumberField,
|
||||
} = traverseFields({
|
||||
adapter,
|
||||
arrayBlockRelations,
|
||||
buildRelationships,
|
||||
columns,
|
||||
fields,
|
||||
@@ -96,6 +95,7 @@ export const buildTable = ({
|
||||
localesIndexes,
|
||||
newTableName: tableName,
|
||||
parentTableName: tableName,
|
||||
relationsToBuild,
|
||||
relationships,
|
||||
}))
|
||||
|
||||
@@ -121,7 +121,7 @@ export const buildTable = ({
|
||||
if (hasLocalizedField) {
|
||||
const localeTableName = `${tableName}_locales`
|
||||
localesColumns.id = serial('id').primaryKey()
|
||||
localesColumns._locale = adapter.enums._locales('_locale').notNull()
|
||||
localesColumns._locale = adapter.enums.enum__locales('_locale').notNull()
|
||||
localesColumns._parentID = parentIDColumnMap[idColType]('_parent_id')
|
||||
.references(() => table.id, { onDelete: 'cascade' })
|
||||
.notNull()
|
||||
@@ -163,7 +163,7 @@ export const buildTable = ({
|
||||
}
|
||||
|
||||
if (hasLocalizedManyNumberField) {
|
||||
columns.locale = adapter.enums._locales('locale')
|
||||
columns.locale = adapter.enums.enum__locales('locale')
|
||||
}
|
||||
|
||||
numbersTable = pgTable(numbersTableName, columns, (cols) => {
|
||||
@@ -206,7 +206,7 @@ export const buildTable = ({
|
||||
}
|
||||
|
||||
if (hasLocalizedRelationshipField) {
|
||||
relationshipColumns.locale = adapter.enums._locales('locale')
|
||||
relationshipColumns.locale = adapter.enums.enum__locales('locale')
|
||||
}
|
||||
|
||||
relationships.forEach((relationTo) => {
|
||||
@@ -273,7 +273,7 @@ export const buildTable = ({
|
||||
const tableRelations = relations(table, ({ many }) => {
|
||||
const result: Record<string, Relation<string>> = {}
|
||||
|
||||
arrayBlockRelations.forEach((val, key) => {
|
||||
relationsToBuild.forEach((val, key) => {
|
||||
result[key] = many(adapter.tables[val])
|
||||
})
|
||||
|
||||
@@ -296,5 +296,5 @@ export const buildTable = ({
|
||||
|
||||
adapter.relations[`relations_${tableName}`] = tableRelations
|
||||
|
||||
return { arrayBlockRelations }
|
||||
return { relationsToBuild }
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import { relations } from 'drizzle-orm'
|
||||
import {
|
||||
PgNumericBuilder,
|
||||
PgVarcharBuilder,
|
||||
index,
|
||||
integer,
|
||||
jsonb,
|
||||
numeric,
|
||||
@@ -28,7 +29,6 @@ import { parentIDColumnMap } from './parentIDColumnMap'
|
||||
|
||||
type Args = {
|
||||
adapter: PostgresAdapter
|
||||
arrayBlockRelations: Map<string, string>
|
||||
buildRelationships: boolean
|
||||
columnPrefix?: string
|
||||
columns: Record<string, PgColumnBuilder>
|
||||
@@ -40,6 +40,7 @@ type Args = {
|
||||
localesIndexes: Record<string, (cols: GenericColumns) => IndexBuilder>
|
||||
newTableName: string
|
||||
parentTableName: string
|
||||
relationsToBuild: Map<string, string>
|
||||
relationships: Set<string>
|
||||
}
|
||||
|
||||
@@ -52,7 +53,6 @@ type Result = {
|
||||
|
||||
export const traverseFields = ({
|
||||
adapter,
|
||||
arrayBlockRelations,
|
||||
buildRelationships,
|
||||
columnPrefix,
|
||||
columns,
|
||||
@@ -64,6 +64,7 @@ export const traverseFields = ({
|
||||
localesIndexes,
|
||||
newTableName,
|
||||
parentTableName,
|
||||
relationsToBuild,
|
||||
relationships,
|
||||
}: Args): Result => {
|
||||
let hasLocalizedField = false
|
||||
@@ -157,7 +158,7 @@ export const traverseFields = ({
|
||||
}
|
||||
|
||||
case 'select': {
|
||||
const enumName = `${newTableName}_${columnPrefix || ''}${toSnakeCase(field.name)}`
|
||||
const enumName = `enum_${newTableName}_${columnPrefix || ''}${toSnakeCase(field.name)}`
|
||||
const fieldName = `${fieldPrefix || ''}${field.name}`
|
||||
|
||||
adapter.enums[enumName] = pgEnum(
|
||||
@@ -172,7 +173,56 @@ export const traverseFields = ({
|
||||
)
|
||||
|
||||
if (field.hasMany) {
|
||||
// build table here
|
||||
const baseColumns: Record<string, PgColumnBuilder> = {
|
||||
order: integer('order').notNull(),
|
||||
parent: parentIDColumnMap[parentIDColType]('parent_id')
|
||||
.references(() => adapter.tables[parentTableName].id, { onDelete: 'cascade' })
|
||||
.notNull(),
|
||||
value: adapter.enums[enumName]('value'),
|
||||
}
|
||||
|
||||
const baseExtraConfig: Record<
|
||||
string,
|
||||
(cols: GenericColumns) => IndexBuilder | UniqueConstraintBuilder
|
||||
> = {}
|
||||
|
||||
if (field.localized) {
|
||||
baseColumns.locale = adapter.enums.enum__locales('locale').notNull()
|
||||
baseExtraConfig.parentOrderLocale = (cols) =>
|
||||
unique().on(cols.parent, cols.order, cols.locale)
|
||||
} else {
|
||||
baseExtraConfig.parent = (cols) => index('parent_idx').on(cols.parent)
|
||||
baseExtraConfig.order = (cols) => index('order_idx').on(cols.order)
|
||||
}
|
||||
|
||||
if (field.index) {
|
||||
baseExtraConfig.value = (cols) => index('value_idx').on(cols.value)
|
||||
}
|
||||
|
||||
const selectTableName = `${newTableName}_${toSnakeCase(fieldName)}`
|
||||
|
||||
buildTable({
|
||||
adapter,
|
||||
baseColumns,
|
||||
baseExtraConfig,
|
||||
fields: [],
|
||||
tableName: selectTableName,
|
||||
})
|
||||
|
||||
relationsToBuild.set(fieldName, selectTableName)
|
||||
|
||||
const selectTableRelations = relations(adapter.tables[selectTableName], ({ one }) => {
|
||||
const result: Record<string, Relation<string>> = {
|
||||
parent: one(adapter.tables[parentTableName], {
|
||||
fields: [adapter.tables[selectTableName].parent],
|
||||
references: [adapter.tables[parentTableName].id],
|
||||
}),
|
||||
}
|
||||
|
||||
return result
|
||||
})
|
||||
|
||||
adapter.relations[`relation_${selectTableName}`] = selectTableRelations
|
||||
} else {
|
||||
targetTable[fieldName] = adapter.enums[enumName](fieldName)
|
||||
}
|
||||
@@ -206,7 +256,7 @@ export const traverseFields = ({
|
||||
|
||||
const arrayTableName = `${newTableName}_${toSnakeCase(field.name)}`
|
||||
|
||||
const { arrayBlockRelations: subArrayBlockRelations } = buildTable({
|
||||
const { relationsToBuild: subRelationsToBuild } = buildTable({
|
||||
adapter,
|
||||
baseColumns,
|
||||
baseExtraConfig,
|
||||
@@ -214,7 +264,7 @@ export const traverseFields = ({
|
||||
tableName: arrayTableName,
|
||||
})
|
||||
|
||||
arrayBlockRelations.set(`${fieldPrefix || ''}${field.name}`, arrayTableName)
|
||||
relationsToBuild.set(`${fieldPrefix || ''}${field.name}`, arrayTableName)
|
||||
|
||||
const arrayTableRelations = relations(adapter.tables[arrayTableName], ({ many, one }) => {
|
||||
const result: Record<string, Relation<string>> = {
|
||||
@@ -228,7 +278,7 @@ export const traverseFields = ({
|
||||
result._locales = many(adapter.tables[`${arrayTableName}_locales`])
|
||||
}
|
||||
|
||||
subArrayBlockRelations.forEach((val, key) => {
|
||||
subRelationsToBuild.forEach((val, key) => {
|
||||
result[key] = many(adapter.tables[val])
|
||||
})
|
||||
|
||||
@@ -266,7 +316,7 @@ export const traverseFields = ({
|
||||
unique().on(cols._parentID, cols._path, cols._order)
|
||||
}
|
||||
|
||||
const { arrayBlockRelations: subArrayBlockRelations } = buildTable({
|
||||
const { relationsToBuild: subRelationsToBuild } = buildTable({
|
||||
adapter,
|
||||
baseColumns,
|
||||
baseExtraConfig,
|
||||
@@ -288,7 +338,7 @@ export const traverseFields = ({
|
||||
result._locales = many(adapter.tables[`${blockTableName}_locales`])
|
||||
}
|
||||
|
||||
subArrayBlockRelations.forEach((val, key) => {
|
||||
subRelationsToBuild.forEach((val, key) => {
|
||||
result[key] = many(adapter.tables[val])
|
||||
})
|
||||
|
||||
@@ -299,7 +349,7 @@ export const traverseFields = ({
|
||||
adapter.relations[`relations_${blockTableName}`] = blockTableRelations
|
||||
}
|
||||
|
||||
arrayBlockRelations.set(`_blocks_${block.slug}`, blockTableName)
|
||||
relationsToBuild.set(`_blocks_${block.slug}`, blockTableName)
|
||||
})
|
||||
|
||||
break
|
||||
@@ -313,7 +363,6 @@ export const traverseFields = ({
|
||||
hasManyNumberField: groupHasManyNumberField,
|
||||
} = traverseFields({
|
||||
adapter,
|
||||
arrayBlockRelations,
|
||||
buildRelationships,
|
||||
columnPrefix: `${columnName}_`,
|
||||
columns,
|
||||
@@ -325,6 +374,7 @@ export const traverseFields = ({
|
||||
localesIndexes,
|
||||
newTableName: `${parentTableName}_${toSnakeCase(field.name)}`,
|
||||
parentTableName,
|
||||
relationsToBuild,
|
||||
relationships,
|
||||
})
|
||||
|
||||
@@ -345,7 +395,6 @@ export const traverseFields = ({
|
||||
hasManyNumberField: tabHasManyNumberField,
|
||||
} = traverseFields({
|
||||
adapter,
|
||||
arrayBlockRelations,
|
||||
buildRelationships,
|
||||
columnPrefix: `${columnPrefix || ''}${toSnakeCase(tab.name)}_`,
|
||||
columns,
|
||||
@@ -356,6 +405,7 @@ export const traverseFields = ({
|
||||
localesIndexes,
|
||||
newTableName: `${parentTableName}_${toSnakeCase(tab.name)}`,
|
||||
parentTableName,
|
||||
relationsToBuild,
|
||||
relationships,
|
||||
})
|
||||
|
||||
@@ -366,7 +416,6 @@ export const traverseFields = ({
|
||||
} else {
|
||||
;({ hasLocalizedField, hasLocalizedRelationshipField } = traverseFields({
|
||||
adapter,
|
||||
arrayBlockRelations,
|
||||
buildRelationships,
|
||||
columnPrefix,
|
||||
columns,
|
||||
@@ -377,6 +426,7 @@ export const traverseFields = ({
|
||||
localesIndexes,
|
||||
newTableName: parentTableName,
|
||||
parentTableName,
|
||||
relationsToBuild,
|
||||
relationships,
|
||||
}))
|
||||
}
|
||||
@@ -393,7 +443,6 @@ export const traverseFields = ({
|
||||
hasManyNumberField,
|
||||
} = traverseFields({
|
||||
adapter,
|
||||
arrayBlockRelations,
|
||||
buildRelationships,
|
||||
columnPrefix,
|
||||
columns,
|
||||
@@ -404,6 +453,7 @@ export const traverseFields = ({
|
||||
localesIndexes,
|
||||
newTableName: parentTableName,
|
||||
parentTableName,
|
||||
relationsToBuild,
|
||||
relationships,
|
||||
}))
|
||||
break
|
||||
|
||||
@@ -22,6 +22,7 @@ export const transformForWrite = ({ data, fields, path = '', tableName }: Args):
|
||||
numbers: [],
|
||||
relationships: [],
|
||||
row: {},
|
||||
selects: {},
|
||||
}
|
||||
|
||||
// This function is responsible for building up the
|
||||
@@ -39,6 +40,7 @@ export const transformForWrite = ({ data, fields, path = '', tableName }: Args):
|
||||
path,
|
||||
relationships: rowToInsert.relationships,
|
||||
row: rowToInsert.row,
|
||||
selects: rowToInsert.selects,
|
||||
})
|
||||
|
||||
return rowToInsert
|
||||
|
||||
28
packages/db-postgres/src/transform/write/selects.ts
Normal file
28
packages/db-postgres/src/transform/write/selects.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
/* eslint-disable no-param-reassign */
|
||||
import { isArrayOfRows } from '../../utilities/isArrayOfRows'
|
||||
|
||||
type Args = {
|
||||
data: unknown
|
||||
locale?: string
|
||||
}
|
||||
|
||||
export const transformSelects = ({ data, locale }: Args) => {
|
||||
const newRows: Record<string, unknown>[] = []
|
||||
|
||||
if (isArrayOfRows(data)) {
|
||||
data.forEach((value, i) => {
|
||||
const newRow: Record<string, unknown> = {
|
||||
order: i + 1,
|
||||
value,
|
||||
}
|
||||
|
||||
if (locale) {
|
||||
newRow.locale = locale
|
||||
}
|
||||
|
||||
newRows.push(newRow)
|
||||
})
|
||||
}
|
||||
|
||||
return newRows
|
||||
}
|
||||
@@ -11,6 +11,7 @@ import { transformArray } from './array'
|
||||
import { transformBlocks } from './blocks'
|
||||
import { transformNumbers } from './numbers'
|
||||
import { transformRelationship } from './relationships'
|
||||
import { transformSelects } from './selects'
|
||||
|
||||
type Args = {
|
||||
arrays: {
|
||||
@@ -33,6 +34,9 @@ type Args = {
|
||||
path: string
|
||||
relationships: Record<string, unknown>[]
|
||||
row: Record<string, unknown>
|
||||
selects: {
|
||||
[tableName: string]: Record<string, unknown>[]
|
||||
}
|
||||
}
|
||||
|
||||
export const traverseFields = ({
|
||||
@@ -50,6 +54,7 @@ export const traverseFields = ({
|
||||
path,
|
||||
relationships,
|
||||
row,
|
||||
selects,
|
||||
}: Args) => {
|
||||
fields.forEach((field) => {
|
||||
let columnName = ''
|
||||
@@ -150,6 +155,7 @@ export const traverseFields = ({
|
||||
path: `${path || ''}${field.name}.`,
|
||||
relationships,
|
||||
row,
|
||||
selects,
|
||||
})
|
||||
})
|
||||
} else {
|
||||
@@ -167,6 +173,7 @@ export const traverseFields = ({
|
||||
path: `${path || ''}${field.name}.`,
|
||||
relationships,
|
||||
row,
|
||||
selects,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -195,6 +202,7 @@ export const traverseFields = ({
|
||||
path: `${path || ''}${tab.name}.`,
|
||||
relationships,
|
||||
row,
|
||||
selects,
|
||||
})
|
||||
})
|
||||
} else {
|
||||
@@ -212,6 +220,7 @@ export const traverseFields = ({
|
||||
path: `${path || ''}${tab.name}.`,
|
||||
relationships,
|
||||
row,
|
||||
selects,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -230,6 +239,7 @@ export const traverseFields = ({
|
||||
path,
|
||||
relationships,
|
||||
row,
|
||||
selects,
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -250,6 +260,7 @@ export const traverseFields = ({
|
||||
path,
|
||||
relationships,
|
||||
row,
|
||||
selects,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -313,6 +324,34 @@ export const traverseFields = ({
|
||||
return
|
||||
}
|
||||
|
||||
if (field.type === 'select' && field.hasMany && Array.isArray(fieldData)) {
|
||||
const selectTableName = `${newTableName}_${toSnakeCase(field.name)}`
|
||||
if (!selects[selectTableName]) selects[selectTableName] = []
|
||||
|
||||
if (field.localized) {
|
||||
if (typeof data[field.name] === 'object' && data[field.name] !== null) {
|
||||
Object.entries(data[field.name]).forEach(([localeKey, localeData]) => {
|
||||
if (Array.isArray(localeData)) {
|
||||
const newRows = transformSelects({
|
||||
data: localeData,
|
||||
locale: localeKey,
|
||||
})
|
||||
|
||||
selects[selectTableName] = selects[selectTableName].concat(newRows)
|
||||
}
|
||||
})
|
||||
}
|
||||
} else {
|
||||
const newRows = transformSelects({
|
||||
data: data[field.name],
|
||||
})
|
||||
|
||||
selects[selectTableName] = selects[selectTableName].concat(newRows)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if (fieldAffectsData(field)) {
|
||||
const valuesToTransform: { localeKey?: string; ref: unknown; value: unknown }[] = []
|
||||
|
||||
|
||||
@@ -32,4 +32,7 @@ export type RowToInsert = {
|
||||
numbers: Record<string, unknown>[]
|
||||
relationships: Record<string, unknown>[]
|
||||
row: Record<string, unknown>
|
||||
selects: {
|
||||
[tableName: string]: Record<string, unknown>[]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,6 +60,7 @@ export const upsertRow = async ({
|
||||
const relationsToInsert: Record<string, unknown>[] = []
|
||||
const numbersToInsert: Record<string, unknown>[] = []
|
||||
const blocksToInsert: { [blockType: string]: BlockRowToInsert[] } = {}
|
||||
const selectsToInsert: { [selectTableName: string]: Record<string, unknown>[] } = {}
|
||||
|
||||
// Maintain a list of promises to run locale, blocks, and relationships
|
||||
// all in parallel
|
||||
@@ -90,6 +91,18 @@ export const upsertRow = async ({
|
||||
})
|
||||
}
|
||||
|
||||
// If there are selects, add parent to each, and then
|
||||
// store by table name and rows
|
||||
if (Object.keys(rowToInsert.selects).length > 0) {
|
||||
Object.entries(rowToInsert.selects).forEach(([selectTableName, selectRows]) => {
|
||||
selectRows.forEach((row) => {
|
||||
row.parent = insertedRow.id
|
||||
if (!selectsToInsert[selectTableName]) selectsToInsert[selectTableName] = []
|
||||
selectsToInsert[selectTableName].push(row)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// If there are blocks, add parent to each, and then
|
||||
// store by table name and rows
|
||||
Object.keys(rowToInsert.blocks).forEach((blockName) => {
|
||||
@@ -230,7 +243,7 @@ export const upsertRow = async ({
|
||||
promises.push(async () => {
|
||||
if (operation === 'update') {
|
||||
await Promise.all(
|
||||
Object.entries(rowToInsert.arrays).map(async ([arrayTableName, tableRows]) => {
|
||||
Object.entries(rowToInsert.arrays).map(async ([arrayTableName]) => {
|
||||
await deleteExistingArrayRows({
|
||||
adapter,
|
||||
parentID: insertedRow.id,
|
||||
@@ -247,6 +260,22 @@ export const upsertRow = async ({
|
||||
})
|
||||
})
|
||||
|
||||
// //////////////////////////////////
|
||||
// INSERT hasMany SELECTS
|
||||
// //////////////////////////////////
|
||||
|
||||
promises.push(async () => {
|
||||
await Promise.all(
|
||||
Object.entries(selectsToInsert).map(async ([selectTableName, tableRows]) => {
|
||||
const selectTable = adapter.tables[selectTableName]
|
||||
if (operation === 'update') {
|
||||
await db.delete(selectTable).where(eq(selectTable.id, insertedRow.id))
|
||||
}
|
||||
await db.insert(selectTable).values(tableRows).returning()
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
await Promise.all(promises.map((promise) => promise()))
|
||||
|
||||
// //////////////////////////////////
|
||||
|
||||
5
packages/db-postgres/src/utilities/appendPrefixToKeys.ts
Normal file
5
packages/db-postgres/src/utilities/appendPrefixToKeys.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export const appendPrefixToObjectKeys = <T>(obj: Record<string, unknown>, prefix: string): T =>
|
||||
Object.entries(obj).reduce((res, [key, val]) => {
|
||||
res[`${prefix}_${key}`] = val
|
||||
return res
|
||||
}, {} as T)
|
||||
Reference in New Issue
Block a user