mirror of
https://github.com/docmost/docmost.git
synced 2026-09-11 07:56:54 +08:00
refactor(favorites): move favorite filtering into the repository query (#2475)
* refactor(favorites): move favorite filtering into the repository query * perf: resolve cleanup targets by primary key
This commit is contained in:
@@ -8,6 +8,7 @@ import { jsonObjectFrom } from 'kysely/helpers/postgres';
|
||||
import { ExpressionBuilder, SelectQueryBuilder, sql } from 'kysely';
|
||||
import { DB } from '@docmost/db/types/db';
|
||||
import { dbOrTx } from '@docmost/db/utils';
|
||||
import { SpaceMemberRepo } from '@docmost/db/repos/space/space-member.repo';
|
||||
|
||||
export const FavoriteType = {
|
||||
PAGE: 'page',
|
||||
@@ -19,7 +20,10 @@ export type FavoriteType = (typeof FavoriteType)[keyof typeof FavoriteType];
|
||||
|
||||
@Injectable()
|
||||
export class FavoriteRepo {
|
||||
constructor(@InjectKysely() private readonly db: KyselyDB) {}
|
||||
constructor(
|
||||
@InjectKysely() private readonly db: KyselyDB,
|
||||
private readonly spaceMemberRepo: SpaceMemberRepo,
|
||||
) {}
|
||||
|
||||
async insert(favorite: InsertableFavorite): Promise<Favorite | undefined> {
|
||||
try {
|
||||
@@ -82,6 +86,8 @@ export class FavoriteRepo {
|
||||
.where('favorites.workspaceId', '=', workspaceId)
|
||||
.where('favorites.type', '=', type);
|
||||
|
||||
query = this.applyMembershipFilter(query, userId);
|
||||
|
||||
if (spaceId) {
|
||||
query = this.applySpaceFilter(query, type, spaceId);
|
||||
}
|
||||
@@ -113,6 +119,8 @@ export class FavoriteRepo {
|
||||
.where('favorites.userId', '=', userId)
|
||||
.where('favorites.workspaceId', '=', workspaceId);
|
||||
|
||||
query = this.applyMembershipFilter(query, userId);
|
||||
|
||||
if (type) {
|
||||
query = query.where('favorites.type', '=', type);
|
||||
}
|
||||
@@ -155,7 +163,7 @@ export class FavoriteRepo {
|
||||
): Promise<void> {
|
||||
if (userIds.length === 0) return;
|
||||
|
||||
const { trx } = opts;
|
||||
const { trx } = opts ?? {};
|
||||
const db = dbOrTx(this.db, trx);
|
||||
|
||||
const usersWithAccess = db
|
||||
@@ -174,7 +182,25 @@ export class FavoriteRepo {
|
||||
await db
|
||||
.deleteFrom('favorites')
|
||||
.where('userId', 'in', userIds)
|
||||
.where('spaceId', '=', spaceId)
|
||||
.where((eb) =>
|
||||
eb.or([
|
||||
eb('spaceId', '=', spaceId),
|
||||
eb.exists(
|
||||
eb
|
||||
.selectFrom('pages')
|
||||
.select(sql`1`.as('one'))
|
||||
.whereRef('pages.id', '=', 'favorites.pageId')
|
||||
.where('pages.spaceId', '=', spaceId),
|
||||
),
|
||||
eb.exists(
|
||||
eb
|
||||
.selectFrom('templates')
|
||||
.select(sql`1`.as('one'))
|
||||
.whereRef('templates.id', '=', 'favorites.templateId')
|
||||
.where('templates.spaceId', '=', spaceId),
|
||||
),
|
||||
]),
|
||||
)
|
||||
.where('userId', 'not in', usersWithAccess)
|
||||
.execute();
|
||||
}
|
||||
@@ -194,6 +220,46 @@ export class FavoriteRepo {
|
||||
.execute();
|
||||
}
|
||||
|
||||
private applyMembershipFilter<Q extends SelectQueryBuilder<any, any, any>>(
|
||||
query: Q,
|
||||
userId: string,
|
||||
): Q {
|
||||
const spaceIds = this.spaceMemberRepo.getUserSpaceIdsQuery(userId);
|
||||
return query.where((eb: any) =>
|
||||
eb.or([
|
||||
eb.and([
|
||||
eb('favorites.type', '=', FavoriteType.SPACE),
|
||||
eb('favorites.spaceId', 'in', spaceIds),
|
||||
]),
|
||||
eb.and([
|
||||
eb('favorites.type', '=', FavoriteType.PAGE),
|
||||
eb.exists(
|
||||
eb
|
||||
.selectFrom('pages')
|
||||
.select(sql`1`.as('one'))
|
||||
.whereRef('pages.id', '=', 'favorites.pageId')
|
||||
.where('pages.spaceId', 'in', spaceIds),
|
||||
),
|
||||
]),
|
||||
eb.and([
|
||||
eb('favorites.type', '=', FavoriteType.TEMPLATE),
|
||||
eb.exists(
|
||||
eb
|
||||
.selectFrom('templates')
|
||||
.select(sql`1`.as('one'))
|
||||
.whereRef('templates.id', '=', 'favorites.templateId')
|
||||
.where((e: any) =>
|
||||
e.or([
|
||||
e('templates.spaceId', 'is', null),
|
||||
e('templates.spaceId', 'in', spaceIds),
|
||||
]),
|
||||
),
|
||||
),
|
||||
]),
|
||||
]),
|
||||
) as Q;
|
||||
}
|
||||
|
||||
private applySpaceFilter<Q extends SelectQueryBuilder<any, any, any>>(
|
||||
query: Q,
|
||||
type: FavoriteType | undefined,
|
||||
@@ -239,7 +305,8 @@ export class FavoriteRepo {
|
||||
'pages.isBase',
|
||||
'pages.spaceId',
|
||||
])
|
||||
.whereRef('pages.id', '=', 'favorites.pageId'),
|
||||
.whereRef('pages.id', '=', 'favorites.pageId')
|
||||
.where(sql.ref('favorites.type'), '=', FavoriteType.PAGE),
|
||||
).as('page');
|
||||
}
|
||||
|
||||
@@ -269,8 +336,8 @@ export class FavoriteRepo {
|
||||
.select(['spaces.id', 'spaces.name', 'spaces.slug', 'spaces.logo'])
|
||||
.where(({ or, ref }) =>
|
||||
or([
|
||||
sql<boolean>`${ref('spaces.id')} = ${ref('favorites.spaceId')}`,
|
||||
sql<boolean>`${ref('spaces.id')} = (SELECT pages.space_id FROM pages WHERE pages.id = ${ref('favorites.pageId')})`,
|
||||
sql<boolean>`${ref('favorites.type')} = ${FavoriteType.SPACE} and ${ref('spaces.id')} = ${ref('favorites.spaceId')}`,
|
||||
sql<boolean>`${ref('favorites.type')} = ${FavoriteType.PAGE} and ${ref('spaces.id')} = (SELECT pages.space_id FROM pages WHERE pages.id = ${ref('favorites.pageId')})`,
|
||||
]),
|
||||
),
|
||||
).as('space');
|
||||
@@ -287,7 +354,8 @@ export class FavoriteRepo {
|
||||
'templates.icon',
|
||||
'templates.spaceId',
|
||||
])
|
||||
.whereRef('templates.id', '=', 'favorites.templateId'),
|
||||
.whereRef('templates.id', '=', 'favorites.templateId')
|
||||
.where(sql.ref('favorites.type'), '=', FavoriteType.TEMPLATE),
|
||||
).as('template');
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user