feat: indexed and optimized folder queries

This commit is contained in:
=
2025-03-19 19:46:05 +05:30
parent 8de7261c9a
commit 003f2b003d
2 changed files with 47 additions and 46 deletions
@@ -0,0 +1,23 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
export async function up(knex: Knex): Promise<void> {
const doesParentColumExist = await knex.schema.hasColumn(TableName.SecretFolder, "parentId");
const doesNameColumnExist = await knex.schema.hasColumn(TableName.SecretFolder, "name");
if (doesParentColumExist && doesNameColumnExist) {
await knex.schema.alterTable(TableName.SecretFolder, (t) => {
t.index(["parentId", "name"]);
});
}
}
export async function down(knex: Knex): Promise<void> {
const doesParentColumExist = await knex.schema.hasColumn(TableName.SecretFolder, "parentId");
const doesNameColumnExist = await knex.schema.hasColumn(TableName.SecretFolder, "name");
if (doesParentColumExist && doesNameColumnExist) {
await knex.schema.alterTable(TableName.SecretFolder, (t) => {
t.dropIndex(["parentId", "name"]);
});
}
}
@@ -1,7 +1,7 @@
import { Knex } from "knex"; import { Knex } from "knex";
import { TDbClient } from "@app/db"; import { TDbClient } from "@app/db";
import { TableName, TProjectEnvironments, TSecretFolders, TSecretFoldersUpdate } from "@app/db/schemas"; import { TableName, TSecretFolders, TSecretFoldersUpdate } from "@app/db/schemas";
import { BadRequestError, DatabaseError } from "@app/lib/errors"; import { BadRequestError, DatabaseError } from "@app/lib/errors";
import { groupBy, removeTrailingSlash } from "@app/lib/fn"; import { groupBy, removeTrailingSlash } from "@app/lib/fn";
import { ormify, selectAllTableCols } from "@app/lib/knex"; import { ormify, selectAllTableCols } from "@app/lib/knex";
@@ -41,12 +41,12 @@ const sqlFindMultipleFolderByEnvPathQuery = (db: Knex, query: Array<{ envId: str
void baseQb void baseQb
.select({ .select({
depth: 1, depth: 1,
// latestFolderVerId: db.raw("NULL::uuid"),
path: db.raw("'/'") path: db.raw("'/'")
}) })
.from(TableName.SecretFolder) .from(TableName.SecretFolder)
.where({ .where({
parentId: null parentId: null,
name: "root"
}) })
.whereIn( .whereIn(
"envId", "envId",
@@ -69,9 +69,7 @@ const sqlFindMultipleFolderByEnvPathQuery = (db: Knex, query: Array<{ envId: str
.where((wb) => .where((wb) =>
formatedQuery.map(({ secretPath }) => formatedQuery.map(({ secretPath }) =>
wb.orWhereRaw( wb.orWhereRaw(
`depth = array_position(ARRAY[${secretPath.map(() => "?").join(",")}]::varchar[], ${ `secret_folders.name = (ARRAY[${secretPath.map(() => "?").join(",")}]::varchar[])[depth]`,
TableName.SecretFolder
}.name,depth)`,
[...secretPath] [...secretPath]
) )
) )
@@ -107,7 +105,6 @@ const sqlFindFolderByPathQuery = (db: Knex, projectId: string, environments: str
void baseQb void baseQb
.select({ .select({
depth: 1, depth: 1,
// latestFolderVerId: db.raw("NULL::uuid"),
path: db.raw("'/'") path: db.raw("'/'")
}) })
.from(TableName.SecretFolder) .from(TableName.SecretFolder)
@@ -117,6 +114,11 @@ const sqlFindFolderByPathQuery = (db: Knex, projectId: string, environments: str
parentId: null parentId: null
}) })
.whereIn(`${TableName.Environment}.slug`, environments) .whereIn(`${TableName.Environment}.slug`, environments)
.select(
db.ref("slug").withSchema(TableName.Environment).as("envSlug"),
db.ref("name").withSchema(TableName.Environment).as("envName"),
db.ref("projectId").withSchema(TableName.Environment)
)
.select(selectAllTableCols(TableName.SecretFolder)) .select(selectAllTableCols(TableName.SecretFolder))
.union( .union(
(qb) => (qb) =>
@@ -128,21 +130,20 @@ const sqlFindFolderByPathQuery = (db: Knex, projectId: string, environments: str
depth: db.raw("parent.depth + 1"), depth: db.raw("parent.depth + 1"),
path: db.raw( path: db.raw(
"CONCAT((CASE WHEN parent.path = '/' THEN '' ELSE parent.path END),'/', secret_folders.name)" "CONCAT((CASE WHEN parent.path = '/' THEN '' ELSE parent.path END),'/', secret_folders.name)"
) ),
envSlug: db.ref("envSlug").withSchema("parent"),
envName: db.ref("envName").withSchema("parent"),
projectId: db.ref("projectId").withSchema("parent")
}) })
.select(selectAllTableCols(TableName.SecretFolder)) .select(selectAllTableCols(TableName.SecretFolder))
.whereRaw( .whereRaw(`secret_folders.name = (ARRAY[${pathSegments.map(() => "?").join(",")}]::varchar[])[depth]`, [
`depth = array_position(ARRAY[${pathSegments ...pathSegments
.map(() => "?") ])
.join(",")}]::varchar[], secret_folders.name,depth)`,
[...pathSegments]
)
.from(TableName.SecretFolder) .from(TableName.SecretFolder)
.join("parent", "parent.id", `${TableName.SecretFolder}.parentId`) .join("parent", "parent.id", `${TableName.SecretFolder}.parentId`)
); );
}) })
.from<TSecretFolders & { depth: number; path: string }>("parent") .from<TSecretFolders & { depth: number; path: string }>("parent")
.leftJoin<TProjectEnvironments>(TableName.Environment, `${TableName.Environment}.id`, "parent.envId")
.select< .select<
(TSecretFolders & { (TSecretFolders & {
depth: number; depth: number;
@@ -152,13 +153,7 @@ const sqlFindFolderByPathQuery = (db: Knex, projectId: string, environments: str
envName: string; envName: string;
projectId: string; projectId: string;
})[] })[]
>( >(selectAllTableCols("parent" as TableName.SecretFolder));
selectAllTableCols("parent" as TableName.SecretFolder),
db.ref("id").withSchema(TableName.Environment).as("envId"),
db.ref("slug").withSchema(TableName.Environment).as("envSlug"),
db.ref("name").withSchema(TableName.Environment).as("envName"),
db.ref("projectId").withSchema(TableName.Environment)
);
}; };
const sqlFindSecretPathByFolderId = (db: Knex, projectId: string, folderIds: string[]) => const sqlFindSecretPathByFolderId = (db: Knex, projectId: string, folderIds: string[]) =>
@@ -220,19 +215,12 @@ export const secretFolderDALFactory = (db: TDbClient) => {
throw new BadRequestError({ throw new BadRequestError({
message: "Invalid secret path. Only alphanumeric characters, dashes, and underscores are allowed." message: "Invalid secret path. Only alphanumeric characters, dashes, and underscores are allowed."
}); });
const formatedPath = removeTrailingSlash(path);
try { try {
const folder = await sqlFindFolderByPathQuery( const query = sqlFindFolderByPathQuery(tx || db.replicaNode(), projectId, [environment], formatedPath)
tx || db.replicaNode(), .where("path", formatedPath)
projectId,
[environment],
removeTrailingSlash(path)
)
.orderBy("depth", "desc")
.first(); .first();
if (folder && folder.path !== removeTrailingSlash(path)) { const folder = await query;
return;
}
if (!folder) return; if (!folder) return;
const { envId: id, envName: name, envSlug: slug, ...el } = folder; const { envId: id, envName: name, envSlug: slug, ...el } = folder;
return { ...el, envId: id, environment: { id, name, slug } }; return { ...el, envId: id, environment: { id, name, slug } };
@@ -250,22 +238,13 @@ export const secretFolderDALFactory = (db: TDbClient) => {
}); });
try { try {
const pathDepth = removeTrailingSlash(path).split("/").filter(Boolean).length + 1; const formatedPath = removeTrailingSlash(path);
const folders = await sqlFindFolderByPathQuery( const folders = await sqlFindFolderByPathQuery(
tx || db.replicaNode(), tx || db.replicaNode(),
projectId, projectId,
environments, environments,
removeTrailingSlash(path) formatedPath
) ).where("path", removeTrailingSlash(path));
.orderBy("depth", "desc")
.where("depth", pathDepth);
const firstFolder = folders[0];
if (firstFolder && firstFolder.path !== removeTrailingSlash(path)) {
return [];
}
return folders.map((folder) => { return folders.map((folder) => {
const { envId: id, envName: name, envSlug: slug, ...el } = folder; const { envId: id, envName: name, envSlug: slug, ...el } = folder;
@@ -323,7 +302,6 @@ export const secretFolderDALFactory = (db: TDbClient) => {
const findSecretPathByFolderIds = async (projectId: string, folderIds: string[], tx?: Knex) => { const findSecretPathByFolderIds = async (projectId: string, folderIds: string[], tx?: Knex) => {
try { try {
const folders = await sqlFindSecretPathByFolderId(tx || db.replicaNode(), projectId, folderIds); const folders = await sqlFindSecretPathByFolderId(tx || db.replicaNode(), projectId, folderIds);
// travelling all the way from leaf node to root contains real path // travelling all the way from leaf node to root contains real path
const rootFolders = groupBy( const rootFolders = groupBy(
folders.filter(({ parentId }) => parentId === null), folders.filter(({ parentId }) => parentId === null),