Browse Source

feat: Add formula functions

Added - UPPER, LOWER, LEN, TRIM

Signed-off-by: Pranav C <61551451+pranavxc@users.noreply.github.com>
pull/448/head
Pranav C 3 years ago committed by Pranav C
parent
commit
1eb3bf97ad
  1. 12
      packages/nc-gui/components/project/spreadsheet/components/editColumn/formulaOptions.vue
  2. 1
      packages/nc-gui/layouts/default.vue
  3. 5
      packages/nocodb/src/lib/dataMapper/lib/sql/formulaQueryBuilderFromString.ts
  4. 44
      packages/nocodb/src/lib/dataMapper/lib/sql/getFunctionName.ts
  5. 11
      packages/nocodb/src/lib/noco/gql/GqlApiBuilder.ts

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

@ -71,7 +71,12 @@ export default {
data: () => ({ data: () => ({
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: ['AVG', 'ADD', 'CONCAT'], availableFunctions: [
'AVG', 'ADD', 'CONCAT', 'TRIM',
'UPPER',
'LOWER',
'LEN'
],
availableBinOps: ['+', '-', '*', '/'], availableBinOps: ['+', '-', '*', '/'],
autocomplete: false, autocomplete: false,
suggestion: null, suggestion: null,
@ -246,7 +251,10 @@ export default {
this.$nextTick(() => { this.$nextTick(() => {
if (this.$refs.sugOptions[this.selected]) { if (this.$refs.sugOptions[this.selected]) {
try { try {
this.$refs.sugList.$el.scrollTo({ top: this.$refs.sugOptions[this.selected].$el.offsetTop, behavior: 'smooth' }) this.$refs.sugList.$el.scrollTo({
top: this.$refs.sugOptions[this.selected].$el.offsetTop,
behavior: 'smooth'
})
} catch (e) { } catch (e) {
} }
} }

1
packages/nc-gui/layouts/default.vue

@ -675,7 +675,6 @@ export default {
toggleTreeviewWindow: 'windows/MutToggleTreeviewWindow' toggleTreeviewWindow: 'windows/MutToggleTreeviewWindow'
}), }),
async loadProjectInfo() { async loadProjectInfo() {
debugger
if (this.$route.params.project_id) { if (this.$route.params.project_id) {
try { try {
const { info } = (await this.$axios.get(`/nc/${this.$route.params.project_id}/projectApiInfo`, { const { info } = (await this.$axios.get(`/nc/${this.$route.params.project_id}/projectApiInfo`, {

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

@ -1,4 +1,5 @@
import jsep from 'jsep'; import jsep from 'jsep';
import getFunctionName from "./getFunctionName";
// todo: switch function based on database // todo: switch function based on database
@ -38,7 +39,6 @@ export default function formulaQueryBuilder(tree, alias, knex, aliasToColumn = {
return fn(pt.arguments[0], a, prevBinaryOp) return fn(pt.arguments[0], a, prevBinaryOp)
} }
break; break;
case 'concat':
case 'CONCAT': case 'CONCAT':
if (knex.clientType() === 'sqlite3') { if (knex.clientType() === 'sqlite3') {
if (pt.arguments.length > 1) { if (pt.arguments.length > 1) {
@ -53,6 +53,9 @@ export default function formulaQueryBuilder(tree, alias, knex, aliasToColumn = {
} }
} }
break; break;
default:
pt.callee.name = getFunctionName(pt.callee.name, knex)
break
} }
return knex.raw(`${pt.callee.name}(${pt.arguments.map(arg => fn(arg).toQuery()).join()})${colAlias}`) return knex.raw(`${pt.callee.name}(${pt.arguments.map(arg => fn(arg).toQuery()).join()})${colAlias}`)

44
packages/nocodb/src/lib/dataMapper/lib/sql/getFunctionName.ts

@ -0,0 +1,44 @@
import {XKnex} from "../../index";
const pg = {
LEN: 'length'
}
const mssql = {
LEN: 'LEN'
}
const mysql2 = {
LEN: 'CHAR_LENGTH'
}
const sqlite3 = {
LEN: 'LENGTH'
}
const getFunctionName = (name, knex: XKnex) => {
switch (knex.clientType()) {
case 'mysql':
case 'mysql2':
return mysql2[name] || name;
break;
case 'pg':
case 'postgre':
return pg[name] || name;
break;
case 'mssql':
return mssql[name] || name;
break;
case 'sqlite':
case 'sqlite3':
return sqlite3[name] || name;
break;
}
}
export default getFunctionName;

11
packages/nocodb/src/lib/noco/gql/GqlApiBuilder.ts

@ -1497,7 +1497,7 @@ export class GqlApiBuilder extends BaseApiBuilder<Noco> implements XcMetaMgr {
public async onTableUpdate(changeObj: any): Promise<void> { public async onTableUpdate(changeObj: any): Promise<void> {
this.log(`onTableUpdate : '%s'`, changeObj.tn); this.log(`onTableUpdate : '%s'`, changeObj.tn);
await super.onTableUpdate(changeObj, async ({ctx, meta}) => { await super.onTableUpdate(changeObj, async ({ctx}) => {
const tn = changeObj.tn; const tn = changeObj.tn;
@ -1515,7 +1515,7 @@ export class GqlApiBuilder extends BaseApiBuilder<Noco> implements XcMetaMgr {
const oldSchema = this.schemas[tn]; const oldSchema = this.schemas[tn];
this.log(`onTableUpdate : Populating new schema for '%s' table`, changeObj.tn); this.log(`onTableUpdate : Populating new schema for '%s' table`, changeObj.tn);
meta.schema = this.schemas[tn] = GqlXcSchemaFactory.create(this.connectionConfig, this.generateRendererArgs(enabledModelCtx)).getString(); this.schemas[tn] = GqlXcSchemaFactory.create(this.connectionConfig, this.generateRendererArgs(enabledModelCtx)).getString();
if (oldSchema !== this.schemas[tn]) { if (oldSchema !== this.schemas[tn]) {
this.log(`onTableUpdate : Updating and taking backup of schema - '%s' table`, changeObj.tn); this.log(`onTableUpdate : Updating and taking backup of schema - '%s' table`, changeObj.tn);
@ -1530,7 +1530,7 @@ export class GqlApiBuilder extends BaseApiBuilder<Noco> implements XcMetaMgr {
} }
await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_models', { await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_models', {
schema: meta.schema, schema: this.schemas[tn],
schema_previous: JSON.stringify(previousSchemas) schema_previous: JSON.stringify(previousSchemas)
}, { }, {
title: tn title: tn
@ -2026,10 +2026,11 @@ export class GqlApiBuilder extends BaseApiBuilder<Noco> implements XcMetaMgr {
} }
await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_models', { await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_models', {
schema: meta.schema, schema: this.schemas[tn],
schema_previous: JSON.stringify(previousSchemas) schema_previous: JSON.stringify(previousSchemas)
}, { }, {
title: tn title: tn,
type:'table'
}); });
} }

Loading…
Cancel
Save