UBERF-8540: Allow derived operations with apply ()

This commit is contained in:
Andrey Sobolev 2024-10-26 21:17:37 +07:00 committed by GitHub
parent 470861ed09
commit e11a0a87cb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 71 additions and 44 deletions
models/notification/src
packages/core/src
plugins
chunter-resources/src/components
notification-resources/src
workbench-resources/src/components
server
core/src
elastic/src
middleware/src
mongo/src
postgres/src
server-storage/src
tool/src

View File

@ -399,6 +399,18 @@ export const notificationOperation: MigrateOperation = {
func: async (client: MigrationClient): Promise<void> => { func: async (client: MigrationClient): Promise<void> => {
await client.update(DOMAIN_DOC_NOTIFY, { '%hash%': { $exists: true } }, { $set: { '%hash%': null } }) await client.update(DOMAIN_DOC_NOTIFY, { '%hash%': { $exists: true } }, { $set: { '%hash%': null } })
} }
},
{
state: 'remove-update-txes-docnotify-ctx',
func: async (client) => {
await client.deleteMany(DOMAIN_TX, {
_class: core.class.TxUpdateDoc,
objectClass: notification.class.DocNotifyContext,
'operations.lastViewedTimestamp': {
$exists: true
}
})
}
} }
]) ])

View File

@ -313,8 +313,8 @@ export class TxOperations implements Omit<Client, 'notify'> {
return this.removeDoc(doc._class, doc.space, doc._id) return this.removeDoc(doc._class, doc.space, doc._id)
} }
apply (scope?: string, measure?: string): ApplyOperations { apply (scope?: string, measure?: string, derived?: boolean): ApplyOperations {
return new ApplyOperations(this, scope, measure, this.isDerived) return new ApplyOperations(this, scope, measure, derived ?? this.isDerived)
} }
async diffUpdate<T extends Doc = Doc>( async diffUpdate<T extends Doc = Doc>(

View File

@ -85,6 +85,8 @@ export interface LowLevelStorage {
rawUpdate: <T extends Doc>(domain: Domain, query: DocumentQuery<T>, operations: DocumentUpdate<T>) => Promise<void> rawUpdate: <T extends Doc>(domain: Domain, query: DocumentQuery<T>, operations: DocumentUpdate<T>) => Promise<void>
rawDeleteMany: <T extends Doc>(domain: Domain, query: DocumentQuery<T>) => Promise<void>
// Traverse documents // Traverse documents
traverse: <T extends Doc>( traverse: <T extends Doc>(
domain: Domain, domain: Domain,

View File

@ -760,7 +760,7 @@
if (unViewed.length === 0) { if (unViewed.length === 0) {
forceRead = true forceRead = true
const op = client.apply(undefined, 'chunter.forceReadContext') const op = client.apply(undefined, 'chunter.forceReadContext', true)
await inboxClient.readDoc(op, object._id) await inboxClient.readDoc(op, object._id)
await op.commit() await op.commit()
} }

View File

@ -419,7 +419,7 @@
if (unViewed.length === 0) { if (unViewed.length === 0) {
forceRead = true forceRead = true
const op = client.apply(undefined, 'chunter.forceReadContext') const op = client.apply(undefined, 'chunter.forceReadContext', true)
await inboxClient.readDoc(op, object._id) await inboxClient.readDoc(op, object._id)
await op.commit() await op.commit()
} }

View File

@ -400,7 +400,7 @@ export async function hideActivityChannels (contexts: DocNotifyContext[]): Promi
export async function readActivityChannels (contexts: DocNotifyContext[]): Promise<void> { export async function readActivityChannels (contexts: DocNotifyContext[]): Promise<void> {
const client = InboxNotificationsClientImpl.getClient() const client = InboxNotificationsClientImpl.getClient()
const notificationsByContext = get(client.inboxNotificationsByContext) const notificationsByContext = get(client.inboxNotificationsByContext)
const ops = getClient().apply(undefined, 'readActivityChannels') const ops = getClient().apply(undefined, 'readActivityChannels', true)
try { try {
for (const context of contexts) { for (const context of contexts) {

View File

@ -235,7 +235,7 @@ export class InboxNotificationsClientImpl implements InboxNotificationsClient {
} }
async archiveAllNotifications (): Promise<void> { async archiveAllNotifications (): Promise<void> {
const ops = getClient().apply(undefined, 'archiveAllNotifications') const ops = getClient().apply(undefined, 'archiveAllNotifications', true)
try { try {
const inboxNotifications = await ops.findAll( const inboxNotifications = await ops.findAll(
@ -260,7 +260,7 @@ export class InboxNotificationsClientImpl implements InboxNotificationsClient {
} }
async readAllNotifications (): Promise<void> { async readAllNotifications (): Promise<void> {
const ops = getClient().apply(undefined, 'readAllNotifications') const ops = getClient().apply(undefined, 'readAllNotifications', true)
try { try {
const inboxNotifications = await ops.findAll( const inboxNotifications = await ops.findAll(
@ -285,7 +285,7 @@ export class InboxNotificationsClientImpl implements InboxNotificationsClient {
} }
async unreadAllNotifications (): Promise<void> { async unreadAllNotifications (): Promise<void> {
const ops = getClient().apply(undefined, 'unreadAllNotifications') const ops = getClient().apply(undefined, 'unreadAllNotifications', true)
try { try {
const inboxNotifications = await ops.findAll( const inboxNotifications = await ops.findAll(

View File

@ -128,7 +128,7 @@ export async function readNotifyContext (doc: DocNotifyContext): Promise<void> {
const inboxClient = InboxNotificationsClientImpl.getClient() const inboxClient = InboxNotificationsClientImpl.getClient()
const inboxNotifications = get(inboxClient.inboxNotificationsByContext).get(doc._id) ?? [] const inboxNotifications = get(inboxClient.inboxNotificationsByContext).get(doc._id) ?? []
const ops = getClient().apply(undefined, 'readNotifyContext') const ops = getClient().apply(undefined, 'readNotifyContext', true)
try { try {
await inboxClient.readNotifications( await inboxClient.readNotifications(
ops, ops,
@ -152,7 +152,7 @@ export async function unReadNotifyContext (doc: DocNotifyContext): Promise<void>
return return
} }
const ops = getClient().apply(undefined, 'unReadNotifyContext') const ops = getClient().apply(undefined, 'unReadNotifyContext', true)
try { try {
await inboxClient.unreadNotifications( await inboxClient.unreadNotifications(

View File

@ -13,24 +13,24 @@
// limitations under the License. // limitations under the License.
--> -->
<script lang="ts"> <script lang="ts">
import { Asset, getResource, translate } from '@hcengineering/platform'
import { ComponentExtensions, getClient, reduceCalls } from '@hcengineering/presentation'
import { import {
AnySvelteComponent, AnySvelteComponent,
closePanel, closePanel,
getCurrentLocation, getCurrentLocation,
Icon, Icon,
ModernTab,
navigate,
languageStore, languageStore,
locationToUrl locationToUrl,
ModernTab,
navigate
} from '@hcengineering/ui' } from '@hcengineering/ui'
import { ComponentExtensions, getClient, reduceCalls } from '@hcengineering/presentation'
import { Asset, getResource, translate } from '@hcengineering/platform'
import { WorkbenchTab } from '@hcengineering/workbench'
import view from '@hcengineering/view' import view from '@hcengineering/view'
import { showMenu } from '@hcengineering/view-resources' import { showMenu } from '@hcengineering/view-resources'
import { WorkbenchTab } from '@hcengineering/workbench'
import { closeTab, getTabDataByLocation, getTabLocation, selectTab, tabIdStore, tabsStore } from '../workbench'
import workbench from '../plugin' import workbench from '../plugin'
import { closeTab, getTabDataByLocation, getTabLocation, selectTab, tabIdStore, tabsStore } from '../workbench'
export let tab: WorkbenchTab export let tab: WorkbenchTab
@ -63,7 +63,9 @@
iconProps = data.iconProps iconProps = data.iconProps
if (tab.name !== name && tab.location === locationToUrl(tabLoc)) { if (tab.name !== name && tab.location === locationToUrl(tabLoc)) {
const op = client.apply(undefined, undefined, true)
await client.diffUpdate(tab, { name }) await client.diffUpdate(tab, { name })
await op.commit()
} }
} }

View File

@ -116,6 +116,8 @@ export class DummyDbAdapter implements DbAdapter {
query: DocumentQuery<T>, query: DocumentQuery<T>,
operations: DocumentUpdate<T> operations: DocumentUpdate<T>
): Promise<void> {} ): Promise<void> {}
async rawDeleteMany<T extends Doc>(domain: Domain, query: DocumentQuery<T>): Promise<void> {}
} }
class InMemoryAdapter extends DummyDbAdapter implements DbAdapter { class InMemoryAdapter extends DummyDbAdapter implements DbAdapter {

View File

@ -36,8 +36,7 @@ const serverCore = plugin(serverCoreId, {
SearchPresenter: '' as Ref<Mixin<SearchPresenter>> SearchPresenter: '' as Ref<Mixin<SearchPresenter>>
}, },
space: { space: {
DocIndexState: '' as Ref<Space>, DocIndexState: '' as Ref<Space>
TriggerState: '' as Ref<Space>
}, },
metadata: { metadata: {
FrontUrl: '' as Metadata<string>, FrontUrl: '' as Metadata<string>,

View File

@ -142,6 +142,10 @@ class ElasticDataAdapter implements DbAdapter {
throw new Error('Method not implemented.') throw new Error('Method not implemented.')
} }
async rawDeleteMany<T extends Doc>(domain: Domain, query: DocumentQuery<T>): Promise<void> {
throw new Error('Method not implemented')
}
async clean (ctx: MeasureContext, domain: Domain, docs: Ref<Doc>[]): Promise<void> { async clean (ctx: MeasureContext, domain: Domain, docs: Ref<Doc>[]): Promise<void> {
const indexExists = await this.client.indices.exists({ const indexExists = await this.client.indices.exists({
index: this.indexName index: this.indexName

View File

@ -19,10 +19,10 @@ import {
FindOptions, FindOptions,
type Doc, type Doc,
type Domain, type Domain,
type Iterator,
type MeasureContext, type MeasureContext,
type Ref, type Ref,
type StorageIterator, type StorageIterator
type Iterator
} from '@hcengineering/core' } from '@hcengineering/core'
import { PlatformError, unknownStatus } from '@hcengineering/platform' import { PlatformError, unknownStatus } from '@hcengineering/platform'
import type { Middleware, PipelineContext } from '@hcengineering/server-core' import type { Middleware, PipelineContext } from '@hcengineering/server-core'
@ -47,36 +47,35 @@ export class LowLevelMiddleware extends BaseMiddleware implements Middleware {
return adapterManager.getAdapter(domain, false).find(ctx, domain, recheck) return adapterManager.getAdapter(domain, false).find(ctx, domain, recheck)
}, },
async load (ctx: MeasureContext, domain: Domain, docs: Ref<Doc>[]): Promise<Doc[]> { load (ctx: MeasureContext, domain: Domain, docs: Ref<Doc>[]): Promise<Doc[]> {
return await adapterManager.getAdapter(domain, false).load(ctx, domain, docs) return adapterManager.getAdapter(domain, false).load(ctx, domain, docs)
}, },
async upload (ctx: MeasureContext, domain: Domain, docs: Doc[]): Promise<void> { upload (ctx: MeasureContext, domain: Domain, docs: Doc[]): Promise<void> {
await adapterManager.getAdapter(domain, true).upload(ctx, domain, docs) return adapterManager.getAdapter(domain, true).upload(ctx, domain, docs)
}, },
async clean (ctx: MeasureContext, domain: Domain, docs: Ref<Doc>[]): Promise<void> { async clean (ctx: MeasureContext, domain: Domain, docs: Ref<Doc>[]): Promise<void> {
await adapterManager.getAdapter(domain, true).clean(ctx, domain, docs) await adapterManager.getAdapter(domain, true).clean(ctx, domain, docs)
}, },
async groupBy<T>(ctx: MeasureContext, domain: Domain, field: string): Promise<Set<T>> { groupBy<T>(ctx: MeasureContext, domain: Domain, field: string): Promise<Set<T>> {
return await adapterManager.getAdapter(domain, false).groupBy(ctx, domain, field) return adapterManager.getAdapter(domain, false).groupBy(ctx, domain, field)
}, },
async rawFindAll<T extends Doc>(domain: Domain, query: DocumentQuery<T>, options?: FindOptions<T>): Promise<T[]> { rawFindAll<T extends Doc>(domain: Domain, query: DocumentQuery<T>, options?: FindOptions<T>): Promise<T[]> {
return await adapterManager.getAdapter(domain, false).rawFindAll(domain, query, options) return adapterManager.getAdapter(domain, false).rawFindAll(domain, query, options)
}, },
async rawUpdate<T extends Doc>( rawUpdate<T extends Doc>(domain: Domain, query: DocumentQuery<T>, operations: DocumentUpdate<T>): Promise<void> {
domain: Domain, return adapterManager.getAdapter(domain, true).rawUpdate(domain, query, operations)
query: DocumentQuery<T>,
operations: DocumentUpdate<T>
): Promise<void> {
await adapterManager.getAdapter(domain, true).rawUpdate(domain, query, operations)
}, },
async traverse<T extends Doc>( rawDeleteMany (domain, query) {
return adapterManager.getAdapter(domain, true).rawDeleteMany(domain, query)
},
traverse<T extends Doc>(
domain: Domain, domain: Domain,
query: DocumentQuery<T>, query: DocumentQuery<T>,
options?: Pick<FindOptions<T>, 'sort' | 'limit' | 'projection'> options?: Pick<FindOptions<T>, 'sort' | 'limit' | 'projection'>
): Promise<Iterator<T>> { ): Promise<Iterator<T>> {
return await adapterManager.getAdapter(domain, false).traverse(domain, query, options) return adapterManager.getAdapter(domain, false).traverse(domain, query, options)
} }
} }
return undefined return undefined

View File

@ -262,6 +262,10 @@ abstract class MongoAdapterBase implements DbAdapter {
} }
} }
async rawDeleteMany<T extends Doc>(domain: Domain, query: DocumentQuery<T>): Promise<void> {
await this.db.collection(domain).deleteMany(this.translateRawQuery(query))
}
abstract init (): Promise<void> abstract init (): Promise<void>
collection<TSchema extends Document = Document>(domain: Domain): Collection<TSchema> { collection<TSchema extends Document = Document>(domain: Domain): Collection<TSchema> {

View File

@ -327,6 +327,13 @@ abstract class PostgresAdapterBase implements DbAdapter {
}) })
} }
async rawDeleteMany<T extends Doc>(domain: Domain, query: DocumentQuery<T>): Promise<void> {
const translatedQuery = this.buildRawQuery(domain, query)
await this.retryTxn(this.client, async (client) => {
await client.query(`DELETE FROM ${translateDomain(domain)} WHERE ${translatedQuery}`)
})
}
async findAll<T extends Doc>( async findAll<T extends Doc>(
ctx: MeasureContext<SessionData>, ctx: MeasureContext<SessionData>,
_class: Ref<Class<T>>, _class: Ref<Class<T>>,

View File

@ -70,6 +70,8 @@ class StorageBlobAdapter implements DbAdapter {
operations: DocumentUpdate<T> operations: DocumentUpdate<T>
): Promise<void> {} ): Promise<void> {}
async rawDeleteMany<T extends Doc>(domain: Domain, query: DocumentQuery<T>): Promise<void> {}
async findAll<T extends Doc>( async findAll<T extends Doc>(
ctx: MeasureContext, ctx: MeasureContext,
_class: Ref<Class<T>>, _class: Ref<Class<T>>,

View File

@ -96,12 +96,6 @@ export class MigrateClientImpl implements MigrationClient {
} }
async deleteMany<T extends Doc>(domain: Domain, query: DocumentQuery<T>): Promise<void> { async deleteMany<T extends Doc>(domain: Domain, query: DocumentQuery<T>): Promise<void> {
const ctx = new MeasureMetricsContext('deleteMany', {}) await this.lowLevel.rawDeleteMany(domain, query)
const docs = await this.lowLevel.rawFindAll(domain, query)
await this.lowLevel.clean(
ctx,
domain,
docs.map((d) => d._id)
)
} }
} }