From 4efece2074edb5dff49a1e420cff90f9915e1a7c Mon Sep 17 00:00:00 2001 From: Andrey Sobolev Date: Fri, 15 Mar 2024 00:12:37 +0700 Subject: [PATCH] Uberf 5986 (#4969) Signed-off-by: Andrey Sobolev --- models/notification/src/migration.ts | 233 ++++++++++++++---------- models/server-activity/src/migration.ts | 2 +- 2 files changed, 136 insertions(+), 99 deletions(-) diff --git a/models/notification/src/migration.ts b/models/notification/src/migration.ts index 48ea1cc525..eff27c48f1 100644 --- a/models/notification/src/migration.ts +++ b/models/notification/src/migration.ts @@ -13,32 +13,32 @@ // limitations under the License. // +import activity, { type ActivityMessage, type DocUpdateMessage } from '@hcengineering/activity' import core, { + DOMAIN_TX, + TxOperations, + TxProcessor, + generateId, type AttachedDoc, type Doc, type Domain, - DOMAIN_TX, - generateId, type Ref, - type TxCollectionCUD, type TxCUD, - TxOperations, - TxProcessor + type TxCollectionCUD } from '@hcengineering/core' import { + tryMigrate, type MigrateOperation, type MigrationClient, - type MigrationUpgradeClient, - tryMigrate + type MigrationUpgradeClient } from '@hcengineering/model' import notification, { + notificationId, type ActivityInboxNotification, type DocNotifyContext, - type DocUpdates, type DocUpdateTx, - notificationId + type DocUpdates } from '@hcengineering/notification' -import activity, { type ActivityMessage, type DocUpdateMessage } from '@hcengineering/activity' import { DOMAIN_NOTIFICATION } from './index' @@ -72,120 +72,157 @@ async function createSpace (client: MigrationUpgradeClient): Promise { async function getActivityMessages ( client: MigrationClient, - tx: DocUpdateTx, - context: DocNotifyContext -): Promise { + contexts: { + context: DocNotifyContext + txes: DocUpdateTx[] + }[] +): Promise { + const result: ActivityInboxNotification[] = [] + const txes = contexts.flatMap((it) => it.txes) const docUpdateMessages = await client.find(DOMAIN_ACTIVITY, { _class: activity.class.DocUpdateMessage, - txId: tx._id, - attachedTo: context.attachedTo + txId: { $in: txes.map((it) => it._id) }, + attachedTo: { $in: contexts.map((it) => it.context.attachedTo) } }) if (docUpdateMessages.length > 0) { - return docUpdateMessages - } - - const originTx = (await client.find>(DOMAIN_TX, { _id: tx._id }))[0] - - if (originTx === undefined) { - return [] - } - - const innerTx = TxProcessor.extractTx(originTx as TxCollectionCUD) as TxCUD - - return ( - await client.find(DOMAIN_ACTIVITY, { - _id: innerTx.objectId as Ref, - attachedTo: context.attachedTo + docUpdateMessages.forEach((message) => { + const ctx = contexts.find((it) => it.context.attachedTo === message.attachedTo) + if (ctx === undefined) { + return + } + const tx = ctx.txes.find((it) => it._id === (message.txId as any)) + if (tx == null) { + return + } + result.push({ + _id: generateId(), + _class: notification.class.ActivityInboxNotification, + space: ctx.context.space, + user: ctx.context.user, + isViewed: !tx.isNew, + attachedTo: message._id, + attachedToClass: message._class, + docNotifyContext: ctx.context._id, + title: tx.title, + body: tx.body, + intlParams: tx.intlParams, + intlParamsNotLocalized: tx.intlParamsNotLocalized, + modifiedOn: tx.modifiedOn, + modifiedBy: tx.modifiedBy, + createdOn: tx.modifiedOn, + createdBy: tx.modifiedBy + }) }) - ).filter(({ _class }) => client.hierarchy.isDerived(_class, activity.class.ActivityMessage)) + } + + const originTx: TxCUD[] = await client.find>(DOMAIN_TX, { _id: { $in: txes.map((it) => it._id) } }) + + if (originTx.length === 0) { + return result + } + + const innerTx = originTx.map((it) => TxProcessor.extractTx(it as TxCollectionCUD) as TxCUD) + + ;( + await client.find(DOMAIN_ACTIVITY, { + _id: { $in: innerTx.map((it) => it.objectId as Ref) } + }) + ) + .filter(({ _class }) => client.hierarchy.isDerived(_class, activity.class.ActivityMessage)) + .forEach((message) => { + const tx = originTx.find((q) => (TxProcessor.extractTx(q) as TxCUD).objectId === message._id) + if (tx == null) { + return + } + + const ctx = contexts.find((it) => it.context.attachedTo === message.attachedTo) + if (ctx === undefined) { + return + } + const docTx = ctx.txes.find((it) => it._id === tx._id) + if (docTx == null) { + return + } + result.push({ + _id: generateId(), + _class: notification.class.ActivityInboxNotification, + space: ctx.context.space, + user: ctx.context.user, + isViewed: !docTx.isNew, + attachedTo: message._id, + attachedToClass: message._class, + docNotifyContext: ctx.context._id, + title: docTx.title, + body: docTx.body, + intlParams: docTx.intlParams, + intlParamsNotLocalized: docTx.intlParamsNotLocalized, + modifiedOn: docTx.modifiedOn, + modifiedBy: docTx.modifiedBy, + createdOn: docTx.modifiedOn, + createdBy: docTx.modifiedBy + }) + }) + return result } -async function getInboxNotifications ( - client: MigrationClient, - tx: DocUpdateTx, - context: DocNotifyContext -): Promise { - const messages = await getActivityMessages(client, tx, context) +async function getInboxData (client: MigrationClient, docUpdates: DocUpdates[]): Promise { + const toProcess = docUpdates.filter((it) => !it.hidden && client.hierarchy.hasClass(it.attachedToClass)) - if (messages.length === 0) { - return [] - } + const contexts = toProcess.map((docUpdate) => { + const newTxIndex = docUpdate.txes.findIndex(({ isNew }) => isNew) - return messages.map((message) => ({ - _id: generateId(), - _class: notification.class.ActivityInboxNotification, - space: context.space, - user: context.user, - isViewed: !tx.isNew, - attachedTo: message._id, - attachedToClass: message._class, - docNotifyContext: context._id, - title: tx.title, - body: tx.body, - intlParams: tx.intlParams, - intlParamsNotLocalized: tx.intlParamsNotLocalized, - modifiedOn: tx.modifiedOn, - modifiedBy: tx.modifiedBy, - createdOn: tx.modifiedOn, - createdBy: tx.modifiedBy + const context: DocNotifyContext = { + _id: docUpdate._id, + _class: notification.class.DocNotifyContext, + space: docUpdate.space, + user: docUpdate.user, + attachedTo: docUpdate.attachedTo, + attachedToClass: docUpdate.attachedToClass, + hidden: docUpdate.hidden, + lastViewedTimestamp: newTxIndex !== -1 ? docUpdate.txes[newTxIndex - 1]?.modifiedOn : docUpdate.lastTxTime, + lastUpdateTimestamp: docUpdate.lastTxTime, + modifiedBy: docUpdate.modifiedBy, + modifiedOn: docUpdate.modifiedOn, + createdBy: docUpdate.createdBy, + createdOn: docUpdate.createdOn + } + + return { + context, + txes: docUpdate.txes + } + }) + + const notifications = await getActivityMessages(client, contexts) + return contexts.map((it) => ({ + context: it.context, + notifications: notifications.filter((nit) => nit.docNotifyContext === it.context._id) })) } -async function getInboxData (client: MigrationClient, docUpdate: DocUpdates): Promise { - if (docUpdate.hidden) { - return - } - - if (!client.hierarchy.hasClass(docUpdate.attachedToClass)) { - console.log('cannot find class: ', docUpdate.attachedToClass) - return - } - - const { txes } = docUpdate - const newTxIndex = txes.findIndex(({ isNew }) => isNew) - - const context: DocNotifyContext = { - _id: docUpdate._id, - _class: notification.class.DocNotifyContext, - space: docUpdate.space, - user: docUpdate.user, - attachedTo: docUpdate.attachedTo, - attachedToClass: docUpdate.attachedToClass, - hidden: docUpdate.hidden, - lastViewedTimestamp: newTxIndex !== -1 ? txes[newTxIndex - 1]?.modifiedOn : docUpdate.lastTxTime, - lastUpdateTimestamp: docUpdate.lastTxTime, - modifiedBy: docUpdate.modifiedBy, - modifiedOn: docUpdate.modifiedOn, - createdBy: docUpdate.createdBy, - createdOn: docUpdate.createdOn - } - - const notifications = (await Promise.all(txes.map((tx) => getInboxNotifications(client, tx, context)))).flat() - - return { - context, - notifications - } -} - async function migrateInboxNotifications (client: MigrationClient): Promise { + let processing = 0 while (true) { const docUpdates = await client.find( DOMAIN_NOTIFICATION, { _class: notification.class.DocUpdates }, - { limit: 500 } + { limit: 1000 } ) + console.log('notifications processing:', processing) + if (docUpdates.length === 0) { return } - const data: InboxData[] = ( - await Promise.all(docUpdates.map((docUpdate) => getInboxData(client, docUpdate))) - ).filter((data): data is InboxData => data !== undefined) + processing += docUpdates.length + + const data: InboxData[] = (await getInboxData(client, docUpdates)).filter( + (data): data is InboxData => data !== undefined + ) await client.deleteMany(DOMAIN_NOTIFICATION, { _id: { $in: docUpdates.map(({ _id }) => _id) } }) await client.create( diff --git a/models/server-activity/src/migration.ts b/models/server-activity/src/migration.ts index ef5474472a..e75e2306df 100644 --- a/models/server-activity/src/migration.ts +++ b/models/server-activity/src/migration.ts @@ -113,7 +113,7 @@ async function createDocUpdateMessages (client: MigrationClient): Promise async function generateFor (_class: Ref>, documents: MigrationIterator): Promise { const classNotFound = new Set() while (true) { - const docs = await documents.next(50) + const docs = await documents.next(100) if (docs == null || docs.length === 0) { break