chore: builds hasMany selects

This commit is contained in:
James
2023-09-19 17:15:10 -04:00
parent 32fb5e66f8
commit bbcdf32a90
10 changed files with 204 additions and 37 deletions

View File

@@ -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,

View File

@@ -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 }
}

View File

@@ -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

View File

@@ -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

View 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
}

View File

@@ -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 }[] = []

View File

@@ -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>[]
}
}

View File

@@ -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()))
// //////////////////////////////////

View 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)