diff --git a/src/lib/server/db/directory.ts b/src/lib/server/db/directory.ts index 432b1dd..6c566e7 100644 --- a/src/lib/server/db/directory.ts +++ b/src/lib/server/db/directory.ts @@ -74,28 +74,6 @@ export const getAllDirectoriesByParent = async (userId: number, parentId: Direct return directories.map(toDirectory); }; -export const getAllRecursiveDirectoriesByParent = async (userId: number, parentId: DirectoryId) => { - const directories = await db - .withRecursive("directory_tree", (db) => - db - .selectFrom("directory") - .selectAll() - .$if(parentId === "root", (qb) => qb.where("parent_id", "is", null)) - .$if(parentId !== "root", (qb) => qb.where("parent_id", "=", parentId as number)) - .where("user_id", "=", userId) - .unionAll((db) => - db - .selectFrom("directory") - .innerJoin("directory_tree", "directory.parent_id", "directory_tree.id") - .selectAll("directory"), - ), - ) - .selectFrom("directory_tree") - .selectAll() - .execute(); - return directories.map(toDirectory); -}; - export const getAllFavoriteDirectories = async (userId: number) => { const directories = await db .selectFrom("directory") @@ -117,6 +95,61 @@ export const getDirectory = async (userId: number, directoryId: number) => { return directory ? toDirectory(directory) : null; }; +export const searchDirectories = async ( + userId: number, + filters: { + parentId: DirectoryId; + inFavorites: boolean; + }, +) => { + const directories = await db + .withRecursive("directory_tree", (db) => + db + .selectFrom("directory") + .select("id") + .where("user_id", "=", userId) + .$if(filters.parentId === "root", (qb) => qb.where((eb) => eb.lit(false))) // directory_tree will be empty if parentId is "root" + .$if(filters.parentId !== "root", (qb) => qb.where("id", "=", filters.parentId as number)) + .unionAll( + db + .selectFrom("directory as d") + .innerJoin("directory_tree as dt", "d.parent_id", "dt.id") + .select("d.id"), + ), + ) + .withRecursive("favorite_directory_tree", (db) => + db + .selectFrom("directory") + .select("id") + .where("user_id", "=", userId) + .$if(!filters.inFavorites, (qb) => qb.where((eb) => eb.lit(false))) // favorite_directory_tree will be empty if inFavorites is false + .$if(filters.inFavorites, (qb) => qb.where("is_favorite", "=", true)) + .unionAll((db) => + db + .selectFrom("directory as d") + .innerJoin("favorite_directory_tree as dt", "d.parent_id", "dt.id") + .select("d.id"), + ), + ) + .selectFrom("directory") + .selectAll() + .where("user_id", "=", userId) + .$if(filters.parentId !== "root", (qb) => + qb.where((eb) => + eb.exists(eb.selectFrom("directory_tree as dt").whereRef("dt.id", "=", "parent_id")), + ), + ) + .$if(filters.inFavorites, (qb) => + qb.where((eb) => + eb.exists( + eb.selectFrom("favorite_directory_tree as dt").whereRef("dt.id", "=", "directory.id"), + ), + ), + ) + .execute(); + return directories.map(toDirectory); +}; + export const setDirectoryEncName = async ( userId: number, directoryId: number, diff --git a/src/lib/server/db/file.ts b/src/lib/server/db/file.ts index af2b6ad..4ad88ab 100644 --- a/src/lib/server/db/file.ts +++ b/src/lib/server/db/file.ts @@ -248,6 +248,7 @@ export const searchFiles = async ( userId: number, filters: { parentId: DirectoryId; + inFavorites: boolean; includeCategoryIds: number[]; excludeCategoryIds: number[]; }, @@ -258,7 +259,7 @@ export const searchFiles = async ( .selectFrom("directory") .select("id") .where("user_id", "=", userId) - .where((eb) => eb.val(filters.parentId !== "root")) // directory_tree will be empty if parentId is "root" + .$if(filters.parentId === "root", (qb) => qb.where((eb) => eb.lit(false))) // directory_tree will be empty if parentId is "root" .$if(filters.parentId !== "root", (qb) => qb.where("id", "=", filters.parentId as number)) .unionAll( db @@ -267,6 +268,20 @@ export const searchFiles = async ( .select("d.id"), ), ) + .withRecursive("favorite_directory_tree", (db) => + db + .selectFrom("directory") + .select("id") + .where("user_id", "=", userId) + .$if(!filters.inFavorites, (qb) => qb.where((eb) => eb.lit(false))) // favorite_directory_tree will be empty if inFavorites is false + .$if(filters.inFavorites, (qb) => qb.where("is_favorite", "=", true)) + .unionAll((db) => + db + .selectFrom("directory as d") + .innerJoin("favorite_directory_tree as dt", "d.parent_id", "dt.id") + .select("d.id"), + ), + ) .withRecursive("include_category_tree", (db) => db .selectFrom("category") @@ -295,19 +310,31 @@ export const searchFiles = async ( ) .selectFrom("file") .selectAll("file") - .$if(filters.parentId === "root", (qb) => qb.where("user_id", "=", userId)) // directory_tree isn't used if parentId is "root" + .where("user_id", "=", userId) .$if(filters.parentId !== "root", (qb) => - qb.where("parent_id", "in", (eb) => eb.selectFrom("directory_tree").select("id")), + qb.where((eb) => + eb.exists(eb.selectFrom("directory_tree as dt").whereRef("dt.id", "=", "file.parent_id")), + ), ) - .where((eb) => - eb.not( - eb.exists( - eb - .selectFrom("file_category") - .whereRef("file_id", "=", "file.id") - .where("category_id", "in", (eb) => - eb.selectFrom("exclude_category_tree").select("id"), - ), + .$if(filters.inFavorites, (qb) => + qb.where((eb) => + eb.or([ + eb("is_favorite", "=", true), + eb.exists( + eb.selectFrom("favorite_directory_tree as dt").whereRef("dt.id", "=", "file.parent_id"), + ), + ]), + ), + ) + .$if(filters.excludeCategoryIds.length > 0, (qb) => + qb.where((eb) => + eb.not( + eb.exists( + eb + .selectFrom("file_category") + .innerJoin("exclude_category_tree", "category_id", "exclude_category_tree.id") + .whereRef("file_id", "=", "file.id"), + ), ), ), ); diff --git a/src/routes/(fullscreen)/search/+page.svelte b/src/routes/(fullscreen)/search/+page.svelte index ef0c447..c2a423e 100644 --- a/src/routes/(fullscreen)/search/+page.svelte +++ b/src/routes/(fullscreen)/search/+page.svelte @@ -37,8 +37,8 @@ includeImages: false, includeVideos: false, includeDirectories: false, - searchInFavorites: false, searchInDirectory: false, + searchInFavorites: false, categories: [], }); let hasCategoryFilter = $derived(filters.categories.length > 0); @@ -47,7 +47,6 @@ filters.includeImages || filters.includeVideos || filters.includeDirectories || - filters.searchInFavorites || filters.name.trim().length > 0, ); @@ -84,9 +83,7 @@ return sortEntries( [...directories, ...files].filter( - (entry) => - (!nameFilter || searchString(entry.name, nameFilter)) && - (!filters.searchInFavorites || entry.isFavorite), + (entry) => !nameFilter || searchString(entry.name, nameFilter), ), ); }); @@ -145,6 +142,7 @@ // Svelte sucks hasAnyFilter; filters.searchInDirectory; + filters.searchInFavorites; filters.categories.length; if (untrack(() => isRestoredFromSnapshot)) { @@ -156,6 +154,7 @@ requestSearch( { ancestorId: filters.searchInDirectory ? data.directoryId! : "root", + inFavorites: filters.searchInFavorites, categories: filters.categories, }, $masterKeyStore?.get(1)?.key!, @@ -216,7 +215,12 @@ {#if hasAnyFilter}
-

검색 결과

+

+ 검색 결과 + {#if result.length > 0} + {" "}({result.length}개) + {/if} +

{#if result.length > 0} { const { directories: directoriesRaw, files: filesRaw } = await trpc().search.search.query({ ancestor: filter.ancestorId, + inFavorites: filter.inFavorites, includeCategories: filter.categories .filter(({ type }) => type === "include") .map(({ info }) => info.id), diff --git a/src/trpc/routers/search.ts b/src/trpc/routers/search.ts index 07caeb2..e47690a 100644 --- a/src/trpc/routers/search.ts +++ b/src/trpc/routers/search.ts @@ -8,6 +8,7 @@ const searchRouter = router({ .input( z.object({ ancestor: DirectoryIdSchema.default("root"), + inFavorites: z.boolean().default(false), includeCategories: z.number().positive().array().default([]), excludeCategories: z.number().positive().array().default([]), }), @@ -15,10 +16,14 @@ const searchRouter = router({ .query(async ({ ctx, input }) => { const [directories, files] = await Promise.all([ input.includeCategories.length === 0 && input.excludeCategories.length === 0 - ? DirectoryRepo.getAllRecursiveDirectoriesByParent(ctx.session.userId, input.ancestor) + ? DirectoryRepo.searchDirectories(ctx.session.userId, { + parentId: input.ancestor, + inFavorites: input.inFavorites, + }) : [], FileRepo.searchFiles(ctx.session.userId, { parentId: input.ancestor, + inFavorites: input.inFavorites, includeCategoryIds: input.includeCategories, excludeCategoryIds: input.excludeCategories, }),