const ParameterizedSQL = require('loopback-connector').ParameterizedSQL; module.exports = Self => { Self.remoteMethod('getLeaves', { description: 'Returns the first shipped and landed possible for params', accepts: [{ arg: 'zoneFk', type: 'Number', required: true, }, { arg: 'parentFk', type: 'Number', default: 1, required: false, }, { arg: 'filter', type: 'Object', description: 'Filter defining where, order, offset, and limit - must be a JSON-encoded string', http: {source: 'query'} }], returns: { type: ['object'], root: true }, http: { path: `/getLeaves`, verb: 'GET' } }); Self.getLeaves = async(zoneFk, parentFk, filter) => { let conn = Self.dataSource.connector; let stmts = []; stmts.push(`DROP TEMPORARY TABLE IF EXISTS tmp.checkedChilds`); stmts.push(new ParameterizedSQL( `CREATE TEMPORARY TABLE tmp.checkedChilds ( id INT, name VARCHAR(100), lft INT, rgt INT, depth BIGINT(22), sons DECIMAL(10, 0), isIncluded TINYINT ) ENGINE = MEMORY`)); if (!parentFk) { stmts.push(new ParameterizedSQL( `INSERT INTO tmp.checkedChilds SELECT zg.id, zg.name, zg.lft, zg.rgt, zg.depth, zg.sons, zi.isIncluded FROM zoneGeo zg JOIN zoneIncluded zi ON zi.geoFk = zg.id AND zoneFk = ?`, [zoneFk])); } let stmt = new ParameterizedSQL( `SELECT * FROM ( SELECT zg.id, zg.name, zg.lft, zg.rgt, zg.depth, zg.sons, IF(ch.id = zg.id, isIncluded, null) selected FROM zoneGeo zg JOIN tmp.checkedChilds ch ON zg.lft <= ch.lft AND zg.rgt >= ch.rgt UNION ALL SELECT child.id, child.name, child.lft, child.rgt, child.depth, child.sons, zi.isIncluded AS selected FROM zoneGeo parent JOIN zoneGeo child ON child.lft > parent.lft AND child.rgt < parent.rgt AND child.depth = parent.depth + 1 LEFT JOIN zoneIncluded zi ON zi.geoFk = child.id AND zi.zoneFk = ? WHERE parent.id = ?) AS nst`, [zoneFk, parentFk]); // Get nodes from depth greather than Origin stmt.merge(conn.makeSuffix(filter)); stmt.merge('GROUP BY nst.id'); const resultIndex = stmts.push(stmt) - 1; const sql = ParameterizedSQL.join(stmts, ';'); const result = await Self.rawStmt(sql); const nodes = result[resultIndex]; if (nodes.length == 0) return nodes; // Get parent nodes const minorDepth = nodes.reduce((a, b) => { return b < a ? b : a; }).depth; const parentNodes = nodes.filter(element => { return element.depth === minorDepth; }); const leaves = Object.assign([], sortNodes(parentNodes)); nestLeaves(leaves); function nestLeaves(elements) { elements.forEach(element => { let childs = Object.assign([], getLeaves(element)); if (childs.length > 0) { element.childs = childs; nestLeaves(element.childs); } }); } function getLeaves(parent) { let elements = nodes.filter(element => { return element.lft > parent.lft && element.rgt < parent.rgt && element.depth === parent.depth + 1; }); return sortNodes(elements); } return leaves; }; function sortNodes(nodes) { return nodes.sort((a, b) => { if (b.selected !== a.selected) { if (a.selected == null) return 1; if (b.selected == null) return -1; return b.selected - a.selected; } return a.name.localeCompare(b.name); }); } };