diff --git a/packages/backend/migration/1707951601000-optimize-emoji-index.js b/packages/backend/migration/1709126576000-optimize-emoji-index.js similarity index 66% rename from packages/backend/migration/1707951601000-optimize-emoji-index.js rename to packages/backend/migration/1709126576000-optimize-emoji-index.js index e1147673e6..e4184895d0 100644 --- a/packages/backend/migration/1707951601000-optimize-emoji-index.js +++ b/packages/backend/migration/1709126576000-optimize-emoji-index.js @@ -3,11 +3,10 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -export class OptimizeEmojiIndex1707951601000 { - name = 'OptimizeEmojiIndex1707951601000' +export class OptimizeEmojiIndex1709126576000 { + name = 'OptimizeEmojiIndex1709126576000' async up(queryRunner) { - await queryRunner.query(`CREATE INDEX "IDX_EMOJI_ALIASES_IDS" ON "emoji" using gin ("aliases")`) await queryRunner.query(`CREATE INDEX "IDX_EMOJI_ROLE_IDS" ON "emoji" using gin ("roleIdsThatCanBeUsedThisEmojiAsReaction")`) await queryRunner.query(`CREATE INDEX "IDX_EMOJI_CATEGORY" ON "emoji" ("category")`) } @@ -15,6 +14,5 @@ export class OptimizeEmojiIndex1707951601000 { async down(queryRunner) { await queryRunner.query(`DROP INDEX "IDX_EMOJI_CATEGORY"`) await queryRunner.query(`DROP INDEX "IDX_EMOJI_ROLE_IDS"`) - await queryRunner.query(`DROP INDEX "IDX_EMOJI_ALIASES_IDS"`) } } diff --git a/packages/backend/src/core/CustomEmojiService.ts b/packages/backend/src/core/CustomEmojiService.ts index a432726ca0..2d195f3b5b 100644 --- a/packages/backend/src/core/CustomEmojiService.ts +++ b/packages/backend/src/core/CustomEmojiService.ts @@ -4,7 +4,7 @@ */ import { Inject, Injectable, OnApplicationShutdown } from '@nestjs/common'; -import { Brackets, In, IsNull, SelectQueryBuilder, WhereExpressionBuilder } from 'typeorm'; +import { Brackets, In, IsNull, ObjectLiteral, SelectQueryBuilder, WhereExpressionBuilder } from 'typeorm'; import * as Redis from 'ioredis'; import { DI } from '@/di-symbols.js'; import { IdService } from '@/core/IdService.js'; @@ -446,9 +446,9 @@ export class CustomEmojiService implements OnApplicationShutdown { @bindThis public async fetchEmojis(params?: FetchEmojisParams) { - function multipleWordsToQuery( + function multipleWordsToQuery( query: string, - builder: SelectQueryBuilder, + builder: SelectQueryBuilder, action: (qb: WhereExpressionBuilder, idx: number, word: string) => void, ) { const words = query.split(/\s/); @@ -514,12 +514,23 @@ export class CustomEmojiService implements OnApplicationShutdown { } if (q.aliases) { // noIndexScan - multipleWordsToQuery(q.aliases, builder, (qb, idx, word) => { - qb.orWhere(`emoji.aliases LIKE :aliases${idx}`, Object.fromEntries([[`aliases${idx}`, `%${word}%`]])); + const subQueryBuilder = builder.subQuery() + .select('COUNT(0)', 'count') + .from( + sq2 => sq2 + .select('unnest(subEmoji.aliases)', 'alias') + .addSelect('subEmoji.id', 'id') + .from('emoji', 'subEmoji'), + 'aliasTable', + ) + .where('"emoji"."id" = "aliasTable"."id"'); + multipleWordsToQuery(q.aliases, subQueryBuilder, (qb, idx, word) => { + qb.orWhere(`"aliasTable"."alias" LIKE :aliases${idx}`, Object.fromEntries([[`aliases${idx}`, `%${word}%`]])); }); + + builder.andWhere(`(${subQueryBuilder.getQuery()}) > 0`); } if (q.category) { - // noIndexScan multipleWordsToQuery(q.category, builder, (qb, idx, word) => { qb.orWhere(`emoji.category LIKE :category${idx}`, Object.fromEntries([[`category${idx}`, `%${word}%`]])); }); diff --git a/packages/frontend/src/pages/admin/custom-emojis-manager.local.list.vue b/packages/frontend/src/pages/admin/custom-emojis-manager.local.list.vue index e884ee8f6f..da04d1e5cc 100644 --- a/packages/frontend/src/pages/admin/custom-emojis-manager.local.list.vue +++ b/packages/frontend/src/pages/admin/custom-emojis-manager.local.list.vue @@ -4,140 +4,132 @@ SPDX-License-Identifier: AGPL-3.0-only --> @@ -360,7 +352,7 @@ const currentPage = ref(0); const queryName = ref(null); const queryCategory = ref(null); -const queryAlias = ref(null); +const queryAliases = ref(null); const queryType = ref(null); const queryLicense = ref(null); const queryUpdatedAtFrom = ref(null); @@ -509,7 +501,7 @@ async function onSearchRequest() { function onQueryResetButtonClicked() { queryName.value = null; queryCategory.value = null; - queryAlias.value = null; + queryAliases.value = null; queryType.value = null; queryLicense.value = null; queryUpdatedAtFrom.value = null; @@ -552,7 +544,7 @@ async function refreshCustomEmojis() { const query: Misskey.entities.AdminEmojiV2ListRequest['query'] = { name: emptyStrToUndefined(queryName.value), type: emptyStrToUndefined(queryType.value), - aliases: emptyStrToUndefined(queryAlias.value), + aliases: emptyStrToUndefined(queryAliases.value), category: emptyStrToUndefined(queryCategory.value), license: emptyStrToUndefined(queryLicense.value), isSensitive: querySensitive.value ? Boolean(querySensitive.value).valueOf() : undefined, diff --git a/packages/frontend/src/pages/admin/custom-emojis-manager.remote.vue b/packages/frontend/src/pages/admin/custom-emojis-manager.remote.vue index 3fc3fe9d2d..4590f060c4 100644 --- a/packages/frontend/src/pages/admin/custom-emojis-manager.remote.vue +++ b/packages/frontend/src/pages/admin/custom-emojis-manager.remote.vue @@ -17,7 +17,6 @@ SPDX-License-Identifier: AGPL-3.0-only