diff --git a/models/core/src/migration.ts b/models/core/src/migration.ts index a9a216656e..82efebbcfa 100644 --- a/models/core/src/migration.ts +++ b/models/core/src/migration.ts @@ -16,40 +16,43 @@ import { saveCollabJson } from '@hcengineering/collaboration' import core, { buildSocialIdString, + configUserAccountUuid, coreId, DOMAIN_MODEL_TX, DOMAIN_SPACE, DOMAIN_STATUS, DOMAIN_TX, generateId, + groupByArray, makeCollabJsonId, makeCollabYdocId, makeDocCollabId, MeasureMetricsContext, RateLimiter, SocialIdType, - type PersonId, + systemAccountUuid, + toIdMap, + TxProcessor, + type AccountUuid, type AnyAttribute, + type AttachedDoc, type Blob, type Class, type Doc, type Domain, type MeasureContext, + type PersonId, type Ref, + type Role, + type SocialKey, type Space, + type SpaceType, type Status, type TxCreateDoc, type TxCUD, - type SpaceType, + type TxMixin, type TxUpdateDoc, - type Role, - toIdMap, - type TypedSpace, - TxProcessor, - type SocialKey, - type AccountUuid, - systemAccountUuid, - configUserAccountUuid + type TypedSpace } from '@hcengineering/core' import { createDefaultSpace, @@ -264,6 +267,41 @@ async function processMigrateContentFor ( } } +async function migrateBackupMixins (client: MigrationClient): Promise { + // Go via classes with domain and check if mixin exists and need to flush %hash% + const hierarchy = client.hierarchy + const curHash = Date.now().toString(16) // Current hash value + + const txIterator = await client.traverse>(DOMAIN_TX, { _class: core.class.TxMixin }) + + while (true) { + const mixinOps = await txIterator.next(500) + if (mixinOps === null || mixinOps.length === 0) break + const _classes = groupByArray(mixinOps, (it) => it.objectClass) + + for (const [_class, ops] of _classes.entries()) { + const domain = hierarchy.findDomain(_class) + if (domain === undefined) continue + let docs = await client.find(domain, { _id: { $in: ops.map((it) => it.objectId) } }) + + docs = docs.filter((it) => { + // Check if mixin is last operation by modifiedOn + const mops = ops.filter((mi) => mi.objectId === it._id) + if (mops.length === 0) return false + return mops.some((mi) => mi.modifiedOn === it.modifiedOn && mi.modifiedBy === it.modifiedBy) + }) + + if (docs.length > 0) { + // Check if docs has mixins from list + const toUpdate = docs.filter((it) => hierarchy.findAllMixins(it).length > 0) + if (toUpdate.length > 0) { + await client.update(domain, { _id: { $in: toUpdate.map((it) => it._id) } }, { '%hash%': curHash }) + } + } + } + } +} + async function migrateCollaborativeDocsToJson (client: MigrationClient): Promise { const ctx = new MeasureMetricsContext('migrateCollaborativeDocsToJson', {}) const storageAdapter = client.storageAdapter @@ -921,6 +959,11 @@ export const coreOperation: MigrateOperation = { state: 'accounts-to-social-ids', mode: 'upgrade', func: migrateAccounts + }, + { + state: 'migrate-backup-mixins', + mode: 'upgrade', + func: migrateBackupMixins } ]) }, diff --git a/server/mongo/src/storage.ts b/server/mongo/src/storage.ts index 79182d055d..def3eb09c5 100644 --- a/server/mongo/src/storage.ts +++ b/server/mongo/src/storage.ts @@ -1401,7 +1401,8 @@ class MongoAdapter extends MongoAdapterBase { const filter = { _id: tx.objectId } const modifyOp = { modifiedBy: tx.modifiedBy, - modifiedOn: tx.modifiedOn + modifiedOn: tx.modifiedOn, + '%hash%': this.curHash() } if (isOperator(tx.attributes)) { const update = { ...this.translateMixinAttrs(tx.mixin, tx.attributes), $set: { ...modifyOp } }