Browse Source

Merge pull request #9532 from titouv/feat/json-formula

feat: Implement formulas for interacting with JSON
pull/9686/head
Mert E. 1 month ago committed by GitHub
parent
commit
1a1721dcda
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 18
      packages/nocodb-sdk/src/lib/formulaHelpers.ts
  2. 11
      packages/nocodb/src/db/functionMappings/mssql.ts
  3. 13
      packages/nocodb/src/db/functionMappings/mysql.ts
  4. 13
      packages/nocodb/src/db/functionMappings/pg.ts
  5. 13
      packages/nocodb/src/db/functionMappings/sqlite.ts
  6. 9
      packages/nocodb/src/helpers/catchError.ts
  7. 4
      packages/nocodb/src/services/columns.service.ts

18
packages/nocodb-sdk/src/lib/formulaHelpers.ts

@ -1379,6 +1379,24 @@ export const formulas: Record<string, FormulaMeta> = {
docsUrl: docsUrl:
'https://docs.nocodb.com/fields/field-types/formula/numeric-functions#value', 'https://docs.nocodb.com/fields/field-types/formula/numeric-functions#value',
}, },
JSON_EXTRACT: {
docsUrl:
'https://docs.nocodb.com/fields/field-types/formula/json-functions#json_extract',
validation: {
args: {
min: 2,
max: 2,
type: [FormulaDataTypes.STRING, FormulaDataTypes.STRING],
},
},
description: 'Extracts a value from a JSON string using a jq-like syntax',
syntax: 'JSON_EXTRACT(json_string, path)',
examples: [
'JSON_EXTRACT(\'{"a": {"b": "c"}}\', \'.a.b\') => "c"',
"JSON_EXTRACT({json_column}, '.key')",
],
returnType: FormulaDataTypes.STRING,
},
// Disabling these functions for now; these act as alias for CreatedAt & UpdatedAt fields; // Disabling these functions for now; these act as alias for CreatedAt & UpdatedAt fields;
// Issue: Error noticed if CreatedAt & UpdatedAt fields are removed from the table after creating these formulas // Issue: Error noticed if CreatedAt & UpdatedAt fields are removed from the table after creating these formulas
// //

11
packages/nocodb/src/db/functionMappings/mssql.ts

@ -209,6 +209,17 @@ const mssql = {
), ),
}; };
}, },
JSON_EXTRACT: async ({ fn, knex, pt, colAlias }: MapFnArgs) => {
return {
builder: knex.raw(
`CASE WHEN ISJSON(${
(await fn(pt.arguments[0])).builder
}) = 1 THEN JSON_VALUE(${(await fn(pt.arguments[0])).builder}, ${
(await fn(pt.arguments[1])).builder
}) ELSE NULL END${colAlias}`,
),
};
},
}; };
export default mssql; export default mssql;

13
packages/nocodb/src/db/functionMappings/mysql.ts

@ -171,6 +171,19 @@ END) ${colAlias}`,
), ),
}; };
}, },
JSON_EXTRACT: async ({ fn, knex, pt, colAlias }: MapFnArgs) => {
return {
builder: knex.raw(
`CASE WHEN JSON_VALID(${
(await fn(pt.arguments[0])).builder
}) = 1 THEN JSON_EXTRACT(${
(await fn(pt.arguments[0])).builder
}, CONCAT('$', ${
(await fn(pt.arguments[1])).builder
})) ELSE NULL END${colAlias}`,
),
};
},
}; };
export default mysql2; export default mysql2;

13
packages/nocodb/src/db/functionMappings/pg.ts

@ -359,6 +359,19 @@ END ${colAlias}`,
), ),
}; };
}, },
JSON_EXTRACT: async ({ fn, knex, pt, colAlias }: MapFnArgs) => {
return {
builder: knex.raw(
`CASE WHEN (${
(await fn(pt.arguments[0])).builder
})::jsonb IS NOT NULL THEN jsonb_path_query_first((${
(await fn(pt.arguments[0])).builder
})::jsonb, CONCAT('$', ${
(await fn(pt.arguments[1])).builder
})::jsonpath) ELSE NULL END${colAlias}`,
),
};
},
}; };
export default pg; export default pg;

13
packages/nocodb/src/db/functionMappings/sqlite.ts

@ -244,6 +244,19 @@ const sqlite3 = {
), ),
}; };
}, },
async JSON_EXTRACT(args: MapFnArgs) {
return {
builder: args.knex.raw(
`CASE WHEN json_valid(${
(await args.fn(args.pt.arguments[0])).builder
}) = 1 THEN json_extract(${
(await args.fn(args.pt.arguments[0])).builder
}, CONCAT('$', ${
(await args.fn(args.pt.arguments[1])).builder
})) ELSE NULL END${args.colAlias}`,
),
};
},
}; };
export default sqlite3; export default sqlite3;

9
packages/nocodb/src/helpers/catchError.ts

@ -636,7 +636,14 @@ const errorHelpers: {
code: 400, code: 400,
}, },
[NcErrorType.FORMULA_ERROR]: { [NcErrorType.FORMULA_ERROR]: {
message: (message: string) => `Formula error: ${message}`, message: (message: string) => {
// try to extract db error - Experimental
if (message.includes(' - ')) {
const [_, dbError] = message.split(' - ');
return `Formula error: ${dbError}`;
}
return `Formula error: ${message}`;
},
code: 400, code: 400,
}, },
[NcErrorType.PERMISSION_DENIED]: { [NcErrorType.PERMISSION_DENIED]: {

4
packages/nocodb/src/services/columns.service.ts

@ -389,7 +389,7 @@ export class ColumnsService {
); );
} catch (e) { } catch (e) {
console.error(e); console.error(e);
NcError.badRequest('Invalid Formula'); throw e;
} }
await Column.update(context, column.id, { await Column.update(context, column.id, {
@ -1843,7 +1843,7 @@ export class ColumnsService {
); );
} catch (e) { } catch (e) {
console.error(e); console.error(e);
NcError.badRequest('Invalid Formula'); throw e;
} }
await Column.insert(context, { await Column.insert(context, {

Loading…
Cancel
Save