Files
plane/web/core/local-db/utils/query-constructor.ts
T

170 lines
5.4 KiB
TypeScript

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;
};