import { getFilteredRowsForGrouping, getIssueFieldsFragment, getMetaKeys, getOrderByFragment, singleFilterConstructor, translateQueryParams, } from "./query.utils"; import { log } from "./utils"; export const SPECIAL_ORDER_BY = { labels__name: "labels", "-labels__name": "labels", assignees__first_name: "members", "-assignees__first_name": "members", issue_module__module__name: "modules", "-issue_module__module__name": "modules", issue_cycle__cycle__name: "cycles", "-issue_cycle__cycle__name": "cycles", state__name: "states", "-state__name": "states", estimate_point__key: "estimate_point", "-estimate_point__key": "estimate_point", }; export const issueFilterQueryConstructor = (workspaceSlug: string, projectId: string, queries: any) => { const { cursor, per_page, group_by, sub_group_by, order_by = "-created_at", ...otherProps } = translateQueryParams(queries); const [pageSize, page, offset] = cursor.split(":"); let sql = ""; const fieldsFragment = getIssueFieldsFragment(); if (sub_group_by) { const orderByString = getOrderByFragment(order_by); sql = getFilteredRowsForGrouping(projectId, queries); sql += `, ranked_issues AS ( SELECT fi.*, ROW_NUMBER() OVER (PARTITION BY group_id, sub_group_id ${orderByString}) as rank, COUNT(*) OVER (PARTITION by group_id, sub_group_id) as total_issues from fi) SELECT ri.*, ${fieldsFragment} FROM ranked_issues ri JOIN issues i ON ri.id = i.id WHERE rank <= ${per_page} `; return sql; } if (group_by) { const orderByString = getOrderByFragment(order_by); sql = getFilteredRowsForGrouping(projectId, queries); sql += `, ranked_issues AS ( SELECT fi.*, ROW_NUMBER() OVER (PARTITION BY group_id ${orderByString}) as rank, COUNT(*) OVER (PARTITION by group_id) as total_issues FROM fi) SELECT ri.*, ${fieldsFragment} FROM ranked_issues ri JOIN issues i ON ri.id = i.id WHERE rank <= ${per_page} `; return sql; } if (order_by && Object.keys(SPECIAL_ORDER_BY).includes(order_by)) { const name = order_by.replace("-", ""); const orderByString = getOrderByFragment(order_by, "i."); sql = `WITH sorted_issues AS (`; sql += getFilteredRowsForGrouping(projectId, queries); sql += `SELECT fi.* , `; if (order_by.includes("assignee")) { sql += ` s.first_name as ${name} `; } else if (order_by.includes("estimate")) { sql += ` s.key as ${name} `; } else { sql += ` s.name as ${name} `; } sql += `FROM fi `; if (order_by && Object.keys(SPECIAL_ORDER_BY).includes(order_by)) { if (order_by.includes("cycle")) { sql += ` LEFT JOIN cycles s on fi.cycle_id = s.id`; } if (order_by.includes("estimate_point__key")) { sql += ` LEFT JOIN estimate_points s on fi.estimate_point = s.id`; } if (order_by.includes("state")) { sql += ` LEFT JOIN states s on fi.state_id = s.id`; } if (order_by.includes("label")) { sql += ` LEFT JOIN issue_meta sm ON fi.id = sm.issue_id AND sm.key = 'label_ids' LEFT JOIN labels s ON s.id = sm.value`; } if (order_by.includes("module")) { sql += ` LEFT JOIN issue_meta sm ON fi.id = sm.issue_id AND sm.key = 'module_ids' LEFT JOIN modules s ON s.id = sm.value`; } if (order_by.includes("assignee")) { sql += ` LEFT JOIN issue_meta sm ON fi.id = sm.issue_id AND sm.key = 'assignee_ids' LEFT JOIN members s ON s.id = sm.value`; } sql += ` ORDER BY ${name} ASC NULLS LAST`; } sql += `)`; sql += `SELECT ${fieldsFragment}, group_concat(si.${name}) as ${name} from sorted_issues si JOIN issues i ON si.id = i.id `; sql += ` group by i.id ${orderByString} LIMIT ${pageSize} OFFSET ${offset * 1 + page * pageSize};`; return sql; } const filterJoinFields = getMetaKeys(queries); const orderByString = getOrderByFragment(order_by); sql = `SELECT ${fieldsFragment}`; if (otherProps.state_group) { sql += `, states.'group' as state_group`; } sql += ` from issues i `; if (otherProps.state_group) { sql += `LEFT JOIN states ON i.state_id = states.id `; } filterJoinFields.forEach((field: string) => { const value = otherProps[field] || ""; sql += ` INNER JOIN issue_meta ${field} ON i.id = ${field}.issue_id AND ${field}.key = '${field}' AND ${field}.value IN ('${value.split(",").join("','")}') `; }); sql += ` WHERE 1=1 `; if (projectId) { sql += ` AND i.project_id = '${projectId}' `; } sql += ` ${singleFilterConstructor(otherProps)} group by i.id `; sql += orderByString; // Add offset and paging to query sql += ` LIMIT ${pageSize} OFFSET ${offset * 1 + page * pageSize};`; return sql; }; export const issueFilterCountQueryConstructor = (workspaceSlug: string, projectId: string, queries: any) => { //@todo Very crude way to extract count from the actual query. Needs to be refactored // Remove group by from the query to fallback to non group query const { group_by, sub_group_by, order_by, ...otherProps } = queries; let sql = issueFilterQueryConstructor(workspaceSlug, projectId, otherProps); const fieldsFragment = getIssueFieldsFragment(); sql = sql.replace(`SELECT ${fieldsFragment}`, "SELECT COUNT(DISTINCT i.id) as total_count"); // Remove everything after group by i.id sql = `${sql.split("group by i.id")[0]};`; return sql; };