Browse Source

feat: Add SWITCH and IF dort in formula

Signed-off-by: Pranav C <pranavxc@gmail.com>
pull/448/head
Pranav C 3 years ago
parent
commit
c2ad42afb6
  1. 2
      packages/nc-gui/components/project/spreadsheet/components/editColumn/formulaOptions.vue
  2. 5
      packages/nc-gui/helpers/formulaList.js
  3. 4
      packages/nocodb/src/lib/dataMapper/lib/sql/formulaQueryBuilderFromString.ts
  4. 28
      packages/nocodb/src/lib/dataMapper/lib/sql/functionMappings/commonFns.ts
  5. 4
      packages/nocodb/src/lib/dataMapper/lib/sql/functionMappings/mssql.ts
  6. 4
      packages/nocodb/src/lib/dataMapper/lib/sql/functionMappings/mysql.ts
  7. 4
      packages/nocodb/src/lib/dataMapper/lib/sql/functionMappings/pg.ts
  8. 28
      packages/nocodb/src/lib/dataMapper/lib/sql/functionMappings/sqlite.ts

2
packages/nc-gui/components/project/spreadsheet/components/editColumn/formulaOptions.vue

@ -73,7 +73,7 @@ export default {
formula: {}, formula: {},
// formulas: ['AVERAGE()', 'COUNT()', 'COUNTA()', 'COUNTALL()', 'SUM()', 'MIN()', 'MAX()', 'AND()', 'OR()', 'TRUE()', 'FALSE()', 'NOT()', 'XOR()', 'ISERROR()', 'IF()', 'LEN()', 'MID()', 'LEFT()', 'RIGHT()', 'FIND()', 'CONCATENATE()', 'T()', 'VALUE()', 'ARRAYJOIN()', 'ARRAYUNIQUE()', 'ARRAYCOMPACT()', 'ARRAYFLATTEN()', 'ROUND()', 'ROUNDUP()', 'ROUNDDOWN()', 'INT()', 'EVEN()', 'ODD()', 'MOD()', 'LOG()', 'EXP()', 'POWER()', 'SQRT()', 'CEILING()', 'FLOOR()', 'ABS()', 'RECORD_ID()', 'CREATED_TIME()', 'ERROR()', 'BLANK()', 'YEAR()', 'MONTH()', 'DAY()', 'HOUR()', 'MINUTE()', 'SECOND()', 'TODAY()', 'NOW()', 'WORKDAY()', 'DATETIME_PARSE()', 'DATETIME_FORMAT()', 'SET_LOCALE()', 'SET_TIMEZONE()', 'DATESTR()', 'TIMESTR()', 'TONOW()', 'FROMNOW()', 'DATEADD()', 'WEEKDAY()', 'WEEKNUM()', 'DATETIME_DIFF()', 'WORKDAY_DIFF()', 'IS_BEFORE()', 'IS_SAME()', 'IS_AFTER()', 'REPLACE()', 'REPT()', 'LOWER()', 'UPPER()', 'TRIM()', 'SUBSTITUTE()', 'SEARCH()', 'SWITCH()', 'LAST_MODIFIED_TIME()', 'ENCODE_URL_COMPONENT()', 'REGEX_EXTRACT()', 'REGEX_MATCH()', 'REGEX_REPLACE()'] // formulas: ['AVERAGE()', 'COUNT()', 'COUNTA()', 'COUNTALL()', 'SUM()', 'MIN()', 'MAX()', 'AND()', 'OR()', 'TRUE()', 'FALSE()', 'NOT()', 'XOR()', 'ISERROR()', 'IF()', 'LEN()', 'MID()', 'LEFT()', 'RIGHT()', 'FIND()', 'CONCATENATE()', 'T()', 'VALUE()', 'ARRAYJOIN()', 'ARRAYUNIQUE()', 'ARRAYCOMPACT()', 'ARRAYFLATTEN()', 'ROUND()', 'ROUNDUP()', 'ROUNDDOWN()', 'INT()', 'EVEN()', 'ODD()', 'MOD()', 'LOG()', 'EXP()', 'POWER()', 'SQRT()', 'CEILING()', 'FLOOR()', 'ABS()', 'RECORD_ID()', 'CREATED_TIME()', 'ERROR()', 'BLANK()', 'YEAR()', 'MONTH()', 'DAY()', 'HOUR()', 'MINUTE()', 'SECOND()', 'TODAY()', 'NOW()', 'WORKDAY()', 'DATETIME_PARSE()', 'DATETIME_FORMAT()', 'SET_LOCALE()', 'SET_TIMEZONE()', 'DATESTR()', 'TIMESTR()', 'TONOW()', 'FROMNOW()', 'DATEADD()', 'WEEKDAY()', 'WEEKNUM()', 'DATETIME_DIFF()', 'WORKDAY_DIFF()', 'IS_BEFORE()', 'IS_SAME()', 'IS_AFTER()', 'REPLACE()', 'REPT()', 'LOWER()', 'UPPER()', 'TRIM()', 'SUBSTITUTE()', 'SEARCH()', 'SWITCH()', 'LAST_MODIFIED_TIME()', 'ENCODE_URL_COMPONENT()', 'REGEX_EXTRACT()', 'REGEX_MATCH()', 'REGEX_REPLACE()']
availableFunctions: formulaList, availableFunctions: formulaList,
availableBinOps: ['+', '-', '*', '/'], availableBinOps: ['+', '-', '*', '/', '>', '==', '<=', '>=', '!='],
autocomplete: false, autocomplete: false,
suggestion: null, suggestion: null,
wordToComplete: '', wordToComplete: '',

5
packages/nc-gui/helpers/formulaList.js

@ -7,7 +7,6 @@ const validations = {
ADD: { ADD: {
validation: { validation: {
args: { min: 1 } args: { min: 1 }
} }
}, },
CONCAT: { CONCAT: {
@ -42,7 +41,9 @@ const validations = {
validation: { args: { rqd: 1 } } validation: { args: { rqd: 1 } }
}, },
SUBSTR: { validation: { args: { min: 2, max: 3 } } }, SUBSTR: { validation: { args: { min: 2, max: 3 } } },
MID: { validation: { args: { rqd: 1 } } } MID: { validation: { args: { rqd: 1 } } },
IF: { validation: { args: { min: 2, max: 3 } } },
SWITCH: { validation: { args: { min: 3 } } }
} }
export default Object.keys(validations) export default Object.keys(validations)

4
packages/nocodb/src/lib/dataMapper/lib/sql/formulaQueryBuilderFromString.ts

@ -66,6 +66,10 @@ export default function formulaQueryBuilder(tree, alias, knex, aliasToColumn = {
} else if (pt.type === 'Identifier') { } else if (pt.type === 'Identifier') {
return knex.raw(`??${colAlias}`, [aliasToColumn[pt.name] || pt.name]); return knex.raw(`??${colAlias}`, [aliasToColumn[pt.name] || pt.name]);
} else if (pt.type === 'BinaryExpression') { } else if (pt.type === 'BinaryExpression') {
if (pt.operator === '==') {
pt.operator = '='
}
const query = knex.raw(`${fn(pt.left, null, pt.operator).toQuery()} ${pt.operator} ${fn(pt.right, null, pt.operator).toQuery()}${colAlias}`) const query = knex.raw(`${fn(pt.left, null, pt.operator).toQuery()} ${pt.operator} ${fn(pt.right, null, pt.operator).toQuery()}${colAlias}`)
if (prevBinaryOp && pt.operator !== prevBinaryOp) { if (prevBinaryOp && pt.operator !== prevBinaryOp) {
query.wrap('(', ')') query.wrap('(', ')')

28
packages/nocodb/src/lib/dataMapper/lib/sql/functionMappings/commonFns.ts

@ -0,0 +1,28 @@
import {MapFnArgs} from "../mapFunctionName";
export default {
// todo: handle default case
SWITCH: (args: MapFnArgs) => {
const count = Math.floor((args.pt.arguments.length-1) / 2)
let query = '';
const switchVal = args.fn(args.pt.arguments[0]).toQuery();
for (let i = 0; i < count; i++) {
query += args.knex.raw(`\n\tWHEN ${args.fn(args.pt.arguments[i * 2 + 1]).toQuery()} THEN ${args.fn(args.pt.arguments[i * 2 + 2]).toQuery()}`).toQuery()
}
if (args.pt.arguments.length % 2 === 0) {
query += args.knex.raw(`\n\tELSE ${args.fn(args.pt.arguments[args.pt.arguments.length - 1]).toQuery()}`).toQuery()
}
return args.knex.raw(`CASE ${switchVal} ${query}\n END${args.colAlias}`)
},
IF: (args: MapFnArgs) => {
let query = args.knex.raw(`\n\tWHEN ${args.fn(args.pt.arguments[0]).toQuery()} THEN ${args.fn(args.pt.arguments[1]).toQuery()}`).toQuery();
if (args.pt.arguments[2]) {
query += args.knex.raw(`\n\tELSE ${args.fn(args.pt.arguments[2]).toQuery()}`).toQuery()
}
return args.knex.raw(`CASE ${query}\n END${args.colAlias}`)
},
TRUE:(_args) => 1,
FALSE:(_args) => 0
}

4
packages/nocodb/src/lib/dataMapper/lib/sql/functionMappings/mssql.ts

@ -1,6 +1,8 @@
import {MapFnArgs} from "../mapFunctionName"; import {MapFnArgs} from "../mapFunctionName";
import commonFns from "./commonFns";
const mssql = { const mssql = {
...commonFns,
MIN: (args: MapFnArgs) => { MIN: (args: MapFnArgs) => {
if (args.pt.arguments.length === 1) { if (args.pt.arguments.length === 1) {
return args.fn(args.pt.arguments[0]) return args.fn(args.pt.arguments[0])
@ -49,7 +51,7 @@ const mssql = {
INT: (args: MapFnArgs) => { INT: (args: MapFnArgs) => {
return args.knex.raw(`CASE WHEN ISNUMERIC(${args.fn(args.pt.arguments[0]).toQuery()}) = 1 THEN FLOOR(${args.fn(args.pt.arguments[0]).toQuery()}) ELSE 0 END${args.colAlias}`) return args.knex.raw(`CASE WHEN ISNUMERIC(${args.fn(args.pt.arguments[0]).toQuery()}) = 1 THEN FLOOR(${args.fn(args.pt.arguments[0]).toQuery()}) ELSE 0 END${args.colAlias}`)
}, },
MID:'SUBSTR' MID:'SUBSTR',
} }
export default mssql; export default mssql;

4
packages/nocodb/src/lib/dataMapper/lib/sql/functionMappings/mysql.ts

@ -1,7 +1,9 @@
import {MapFnArgs} from "../mapFunctionName"; import {MapFnArgs} from "../mapFunctionName";
import commonFns from "./commonFns";
const mysql2 = { const mysql2 = {
...commonFns,
LEN: 'CHAR_LENGTH', LEN: 'CHAR_LENGTH',
MIN: 'LEAST', MIN: 'LEAST',
MAX: 'GREATEST', MAX: 'GREATEST',
@ -20,7 +22,7 @@ const mysql2 = {
RIGHT:(args: MapFnArgs)=> { RIGHT:(args: MapFnArgs)=> {
return args.knex.raw(`SUBSTR(${args.fn(args.pt.arguments[0])},-${args.fn(args.pt.arguments[1])})${args.colAlias}`) return args.knex.raw(`SUBSTR(${args.fn(args.pt.arguments[0])},-${args.fn(args.pt.arguments[1])})${args.colAlias}`)
}, },
MID:'SUBSTR' MID:'SUBSTR',
} }

4
packages/nocodb/src/lib/dataMapper/lib/sql/functionMappings/pg.ts

@ -1,6 +1,8 @@
import {MapFnArgs} from "../mapFunctionName"; import {MapFnArgs} from "../mapFunctionName";
import commonFns from "./commonFns";
const pg = { const pg = {
...commonFns,
LEN: 'length', LEN: 'length',
MIN: 'least', MIN: 'least',
MAX: 'greatest', MAX: 'greatest',
@ -15,7 +17,7 @@ const pg = {
// todo: correction // todo: correction
return args.knex.raw(`REGEXP_REPLACE(COALESCE(${args.fn(args.pt.arguments[0])}::character varying, '0'), '[^0-9]+|\\.[0-9]+' ,'')${args.colAlias}`) return args.knex.raw(`REGEXP_REPLACE(COALESCE(${args.fn(args.pt.arguments[0])}::character varying, '0'), '[^0-9]+|\\.[0-9]+' ,'')${args.colAlias}`)
}, },
MID: 'SUBSTR' MID: 'SUBSTR',
} }

28
packages/nocodb/src/lib/dataMapper/lib/sql/functionMappings/sqlite.ts

@ -1,38 +1,38 @@
import {MapFnArgs} from "../mapFunctionName"; import {MapFnArgs} from "../mapFunctionName";
import commonFns from "./commonFns";
const sqlite3 = { const sqlite3 = {
...commonFns,
LEN: 'LENGTH', LEN: 'LENGTH',
CEILING(_pt) { CEILING(args) {
// todo: return args.knex.raw(`round(${args.fn(args.pt.arguments[0])} + 0.5)${args.colAlias}`)
}, FLOOR(_pt) { }, FLOOR(args) {
// todo: return args.knex.raw(`round(${args.fn(args.pt.arguments[0])} - 0.5)${args.colAlias}`)
}, },
MOD:(pt) => { MOD: (args: MapFnArgs) => {
Object.assign(pt, { return args.fn({
type: 'BinaryExpression', type: 'BinaryExpression',
operator: '%', operator: '%',
left: pt.arguments[0], left: args.pt.arguments[0],
right: pt.arguments[1] right: args.pt.arguments[1]
}) })
}, },
REPEAT(args: MapFnArgs) { REPEAT(args: MapFnArgs) {
return args.knex.raw(`replace(printf('%.' || ${args.fn(args.pt.arguments[1])} || 'c', '/'),'/',${args.fn(args.pt.arguments[0])})${args.colAlias}`) return args.knex.raw(`replace(printf('%.' || ${args.fn(args.pt.arguments[1])} || 'c', '/'),'/',${args.fn(args.pt.arguments[0])})${args.colAlias}`)
}, },
NOW: 'DATE', NOW: 'DATE',
SEARCH:'INSTR', SEARCH: 'INSTR',
INT(args: MapFnArgs) { INT(args: MapFnArgs) {
return args.knex.raw(`CAST(${args.fn(args.pt.arguments[0])} as INTEGER)${args.colAlias}`) return args.knex.raw(`CAST(${args.fn(args.pt.arguments[0])} as INTEGER)${args.colAlias}`)
}, },
LEFT: (args: MapFnArgs) => {
LEFT:(args: MapFnArgs)=> {
return args.knex.raw(`SUBSTR(${args.fn(args.pt.arguments[0])},1,${args.fn(args.pt.arguments[1])})${args.colAlias}`) return args.knex.raw(`SUBSTR(${args.fn(args.pt.arguments[0])},1,${args.fn(args.pt.arguments[1])})${args.colAlias}`)
}, },
RIGHT:(args: MapFnArgs)=> { RIGHT: (args: MapFnArgs) => {
return args.knex.raw(`SUBSTR(${args.fn(args.pt.arguments[0])},-${args.fn(args.pt.arguments[1])})${args.colAlias}`) return args.knex.raw(`SUBSTR(${args.fn(args.pt.arguments[0])},-${args.fn(args.pt.arguments[1])})${args.colAlias}`)
}, },
MID:'SUBSTR' MID: 'SUBSTR',
} }

Loading…
Cancel
Save