-
-
Notifications
You must be signed in to change notification settings - Fork 7.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
chore: add document-id migration (#19514)
* chore: add document-id migration * fix: use database identifiers utils * fix: leave rename identifiers migration name as it was before * chore: revert database config * feat: simplify migration document ids query * update with subquery --------- Co-authored-by: Marc-Roig <marc12info@gmail.com> Co-authored-by: Marc Roig <marc.roig.campos@strapi.io>
- Loading branch information
1 parent
84ed16e
commit 1122223
Showing
5 changed files
with
186 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
178 changes: 178 additions & 0 deletions
178
packages/core/database/src/migrations/internal-migrations/5.0.0-02-document-id.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,178 @@ | ||
import { createId } from '@paralleldrive/cuid2'; | ||
import type { Knex } from 'knex'; | ||
|
||
import type { Migration } from '../common'; | ||
import type { Database } from '../..'; | ||
import type { Meta } from '../../metadata'; | ||
import { identifiers } from '../../utils/identifiers'; | ||
|
||
interface Params { | ||
joinColumn: string; | ||
inverseJoinColumn: string; | ||
tableName: string; | ||
joinTableName: string; | ||
} | ||
|
||
const QUERIES = { | ||
async postgres(knex: Knex, params: Params) { | ||
const res = await knex.raw( | ||
` | ||
SELECT :tableName:.id as id, string_agg(DISTINCT :inverseJoinColumn:::character varying, ',') as other_ids | ||
FROM :tableName: | ||
LEFT JOIN :joinTableName: ON :tableName:.id = :joinTableName:.:joinColumn: | ||
WHERE document_id IS NULL | ||
GROUP BY :tableName:.id, :joinColumn: | ||
LIMIT 1; | ||
`, | ||
params | ||
); | ||
|
||
return res.rows; | ||
}, | ||
async mysql(knex: Knex, params: Params) { | ||
const [res] = await knex.raw( | ||
` | ||
SELECT :tableName:.id as id, group_concat(DISTINCT :inverseJoinColumn:) as other_ids | ||
FROM :tableName: | ||
LEFT JOIN :joinTableName: ON :tableName:.id = :joinTableName:.:joinColumn: | ||
WHERE document_id IS NULL | ||
GROUP BY :tableName:.id, :joinColumn: | ||
LIMIT 1; | ||
`, | ||
params | ||
); | ||
|
||
return res; | ||
}, | ||
async sqlite(knex: Knex, params: Params) { | ||
return knex.raw( | ||
` | ||
SELECT :tableName:.id as id, group_concat(DISTINCT :inverseJoinColumn:) as other_ids | ||
FROM :tableName: | ||
LEFT JOIN :joinTableName: ON :tableName:.id = :joinTableName:.:joinColumn: | ||
WHERE document_id IS NULL | ||
GROUP BY :joinColumn: | ||
LIMIT 1; | ||
`, | ||
params | ||
); | ||
}, | ||
}; | ||
|
||
const getNextIdsToCreateDocumentId = async ( | ||
db: Database, | ||
knex: Knex, | ||
{ | ||
joinColumn, | ||
inverseJoinColumn, | ||
tableName, | ||
joinTableName, | ||
}: { | ||
joinColumn: string; | ||
inverseJoinColumn: string; | ||
tableName: string; | ||
joinTableName: string; | ||
} | ||
): Promise<number[]> => { | ||
const res = await QUERIES[db.dialect.client as keyof typeof QUERIES](knex, { | ||
joinColumn, | ||
inverseJoinColumn, | ||
tableName, | ||
joinTableName, | ||
}); | ||
|
||
if (res.length > 0) { | ||
const row = res[0]; | ||
const otherIds = row.other_ids | ||
? row.other_ids.split(',').map((v: string) => parseInt(v, 10)) | ||
: []; | ||
|
||
return [row.id, ...otherIds]; | ||
} | ||
|
||
return []; | ||
}; | ||
|
||
// Migrate document ids for tables that have localizations | ||
const migrateDocumentIdsWithLocalizations = async (db: Database, knex: Knex, meta: Meta) => { | ||
const singularName = meta.singularName.toLowerCase(); | ||
const joinColumn = identifiers.getJoinColumnAttributeIdName(singularName); | ||
const inverseJoinColumn = identifiers.getInverseJoinColumnAttributeIdName(singularName); | ||
|
||
let ids: number[]; | ||
|
||
do { | ||
ids = await getNextIdsToCreateDocumentId(db, knex, { | ||
joinColumn, | ||
inverseJoinColumn, | ||
tableName: meta.tableName, | ||
joinTableName: identifiers.getJoinTableName(meta.tableName, `localizations`), | ||
}); | ||
|
||
if (ids.length > 0) { | ||
await knex(meta.tableName).update({ document_id: createId() }).whereIn('id', ids); | ||
} | ||
} while (ids.length > 0); | ||
}; | ||
|
||
// Migrate document ids for tables that don't have localizations | ||
const migrationDocumentIds = async (db: Database, knex: Knex, meta: Meta) => { | ||
let run = true; | ||
|
||
do { | ||
const updatedRows = await knex(meta.tableName) | ||
.update({ document_id: createId() }) | ||
.whereIn('id', (builder) => { | ||
return builder.whereNull('document_id').select('id').limit(1); | ||
}); | ||
|
||
if (updatedRows <= 0) { | ||
run = false; | ||
} | ||
} while (run); | ||
}; | ||
|
||
const createDocumentIdColumn = async (knex: Knex, tableName: string) => { | ||
await knex.schema.alterTable(tableName, (table) => { | ||
table.string('document_id'); | ||
}); | ||
}; | ||
|
||
const hasLocalizationsJoinTable = async (knex: Knex, tableName: string) => { | ||
const joinTableName = identifiers.getJoinTableName(tableName, 'localizations'); | ||
return knex.schema.hasTable(joinTableName); | ||
}; | ||
|
||
export const createdDocumentId: Migration = { | ||
name: '5.0.0-02-created-document-id', | ||
async up(knex, db) { | ||
// do sth | ||
for (const meta of db.metadata.values()) { | ||
const hasTable = await knex.schema.hasTable(meta.tableName); | ||
|
||
if (!hasTable) { | ||
continue; | ||
} | ||
|
||
if ('documentId' in meta.attributes) { | ||
// add column if doesn't exist | ||
const hasDocumentIdColumn = await knex.schema.hasColumn(meta.tableName, 'document_id'); | ||
|
||
if (hasDocumentIdColumn) { | ||
continue; | ||
} | ||
|
||
await createDocumentIdColumn(knex, meta.tableName); | ||
|
||
if (await hasLocalizationsJoinTable(knex, meta.tableName)) { | ||
await migrateDocumentIdsWithLocalizations(db, knex, meta); | ||
} else { | ||
await migrationDocumentIds(db, knex, meta); | ||
} | ||
} | ||
} | ||
}, | ||
async down() { | ||
throw new Error('not implemented'); | ||
}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters