From 30b505aa8fa4f8b0640f3a233acb0d51fe5d4c32 Mon Sep 17 00:00:00 2001 From: Javier Segarra Date: Wed, 17 Jan 2024 08:34:15 +0100 Subject: [PATCH 1/9] refs #6694 feat: add filter for postcodes --- back/models/postcode.js | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/back/models/postcode.js b/back/models/postcode.js index b08fdaa40..4c1bf7fd7 100644 --- a/back/models/postcode.js +++ b/back/models/postcode.js @@ -1,6 +1,38 @@ let UserError = require('vn-loopback/util/user-error'); module.exports = Self => { + Self.remoteMethod('filter', { + description: + 'Find all postcodes of the model matched by postcode, town, province or country.', + accessType: 'READ', + returns: { + type: ['object'], + root: true, + }, + http: { + path: `/filter`, + verb: 'GET', + }, + }); + Self.filter = async(ctx, filter, options) => { + const {Postcode} = Self.app.models; + let {value} = ctx.where; + let limit = ctx.where?.limit ?? 30; + return await Postcode.rawSql(` + SELECT pc.code, t.name as town, p.name as province , c.country + FROM postCode pc + JOIN town t on t.id = pc.townFk + JOIN province p on p.id = t.provinceFk + JOIN country c on c.id = p.countryFk + WHERE + pc.code like '%${value}%' + or t.name like '%${value}%' + or p.name like '%${value}%' + or c.country like '%${value}%' + LIMIT ${limit} + `); + }; + Self.rewriteDbError(function(err) { if (err.code === 'ER_DUP_ENTRY') return new UserError(`This postcode already exists`); From d47429b4a7db95f8d549ac858652288f218dca71 Mon Sep 17 00:00:00 2001 From: carlossa Date: Wed, 17 Jan 2024 10:01:38 +0100 Subject: [PATCH 2/9] refs #6667 sql --- modules/worker/back/methods/worker/search.js | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/modules/worker/back/methods/worker/search.js b/modules/worker/back/methods/worker/search.js index 7fe9e0666..2cef3543b 100644 --- a/modules/worker/back/methods/worker/search.js +++ b/modules/worker/back/methods/worker/search.js @@ -41,13 +41,15 @@ module.exports = Self => { } const stmt = new ParameterizedSQL(` - SELECT * - FROM( - SELECT DISTINCT w.id, w.code, u.name, u.nickname, u.active, b.departmentFk - FROM worker w - JOIN account.user u ON u.id = w.id - LEFT JOIN business b ON b.workerFk = w.id - ) w`); + SELECT * + FROM( + SELECT w.id, w.code, u.name, u.nickname, u.active, b.departmentFk + FROM worker w + JOIN account.user u ON u.id = w.id + LEFT JOIN business b ON b.workerFk = w.id + ORDER BY b.id DESC + ) w + GROUP BY w.id`); stmt.merge(conn.makeSuffix(filter)); return conn.executeStmt(stmt); From d1a3c67d2ea296971c4f7390be827cfe554ff2f9 Mon Sep 17 00:00:00 2001 From: carlossa Date: Wed, 17 Jan 2024 14:07:04 +0100 Subject: [PATCH 3/9] refs #6667 fix limit --- modules/worker/back/methods/worker/search.js | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/modules/worker/back/methods/worker/search.js b/modules/worker/back/methods/worker/search.js index 2cef3543b..e423f660d 100644 --- a/modules/worker/back/methods/worker/search.js +++ b/modules/worker/back/methods/worker/search.js @@ -42,14 +42,15 @@ module.exports = Self => { const stmt = new ParameterizedSQL(` SELECT * - FROM( - SELECT w.id, w.code, u.name, u.nickname, u.active, b.departmentFk - FROM worker w - JOIN account.user u ON u.id = w.id - LEFT JOIN business b ON b.workerFk = w.id - ORDER BY b.id DESC - ) w - GROUP BY w.id`); + FROM( + SELECT w.id, w.code, u.name, u.nickname, u.active, b.departmentFk + FROM worker w + JOIN account.user u ON u.id = w.id + LEFT JOIN business b ON b.workerFk = w.id + ORDER BY b.id DESC + LIMIT 10000000000000000000 + ) w + GROUP BY w.id`); stmt.merge(conn.makeSuffix(filter)); return conn.executeStmt(stmt); From 71f387f626add3952755e0c83b6ead88bd435941 Mon Sep 17 00:00:00 2001 From: Javier Segarra Date: Wed, 17 Jan 2024 14:15:21 +0100 Subject: [PATCH 4/9] refs #6694 feat: move filter remote method to own file --- back/methods/postcode/filter.js | 34 +++++++++++++++++++++++++++++++++ back/models/postcode.js | 33 +------------------------------- 2 files changed, 35 insertions(+), 32 deletions(-) create mode 100644 back/methods/postcode/filter.js diff --git a/back/methods/postcode/filter.js b/back/methods/postcode/filter.js new file mode 100644 index 000000000..53f25b7d4 --- /dev/null +++ b/back/methods/postcode/filter.js @@ -0,0 +1,34 @@ + +module.exports = Self => { + Self.remoteMethod('filter', { + description: + 'Find all postcodes of the model matched by postcode, town, province or country.', + accessType: 'READ', + returns: { + type: ['object'], + root: true, + }, + http: { + path: `/filter`, + verb: 'GET', + }, + }); + Self.filter = async(ctx, filter, options) => { + const {Postcode} = Self.app.models; + let {value} = ctx.where; + let limit = ctx.where?.limit ?? 30; + return await Postcode.rawSql(` + SELECT pc.code, t.name as town, p.name as province , c.country + FROM postCode pc + JOIN town t on t.id = pc.townFk + JOIN province p on p.id = t.provinceFk + JOIN country c on c.id = p.countryFk + WHERE + pc.code like '%${value}%' + or t.name like '%${value}%' + or p.name like '%${value}%' + or c.country like '%${value}%' + LIMIT ${limit} + `); + }; +}; diff --git a/back/models/postcode.js b/back/models/postcode.js index 4c1bf7fd7..63fd0657f 100644 --- a/back/models/postcode.js +++ b/back/models/postcode.js @@ -1,38 +1,7 @@ let UserError = require('vn-loopback/util/user-error'); module.exports = Self => { - Self.remoteMethod('filter', { - description: - 'Find all postcodes of the model matched by postcode, town, province or country.', - accessType: 'READ', - returns: { - type: ['object'], - root: true, - }, - http: { - path: `/filter`, - verb: 'GET', - }, - }); - Self.filter = async(ctx, filter, options) => { - const {Postcode} = Self.app.models; - let {value} = ctx.where; - let limit = ctx.where?.limit ?? 30; - return await Postcode.rawSql(` - SELECT pc.code, t.name as town, p.name as province , c.country - FROM postCode pc - JOIN town t on t.id = pc.townFk - JOIN province p on p.id = t.provinceFk - JOIN country c on c.id = p.countryFk - WHERE - pc.code like '%${value}%' - or t.name like '%${value}%' - or p.name like '%${value}%' - or c.country like '%${value}%' - LIMIT ${limit} - `); - }; - + require('../methods/postcode/filter.js')(Self); Self.rewriteDbError(function(err) { if (err.code === 'ER_DUP_ENTRY') return new UserError(`This postcode already exists`); From 92a3e841ed3fe6ec34ea6ff44339808bd6a71e30 Mon Sep 17 00:00:00 2001 From: Javier Segarra Date: Wed, 17 Jan 2024 14:16:09 +0100 Subject: [PATCH 5/9] refs #6694 perf: change let to const --- back/methods/postcode/filter.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/back/methods/postcode/filter.js b/back/methods/postcode/filter.js index 53f25b7d4..0c97c61c5 100644 --- a/back/methods/postcode/filter.js +++ b/back/methods/postcode/filter.js @@ -15,8 +15,8 @@ module.exports = Self => { }); Self.filter = async(ctx, filter, options) => { const {Postcode} = Self.app.models; - let {value} = ctx.where; - let limit = ctx.where?.limit ?? 30; + const {value} = ctx.where; + const limit = ctx.where?.limit ?? 30; return await Postcode.rawSql(` SELECT pc.code, t.name as town, p.name as province , c.country FROM postCode pc From ccdef0458a566849a5381e6357a39843cfe508e3 Mon Sep 17 00:00:00 2001 From: carlossa Date: Wed, 17 Jan 2024 14:39:11 +0100 Subject: [PATCH 6/9] refs #6667 fix groupBy --- modules/worker/back/methods/worker/search.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/modules/worker/back/methods/worker/search.js b/modules/worker/back/methods/worker/search.js index e423f660d..59348a9ce 100644 --- a/modules/worker/back/methods/worker/search.js +++ b/modules/worker/back/methods/worker/search.js @@ -49,10 +49,14 @@ module.exports = Self => { LEFT JOIN business b ON b.workerFk = w.id ORDER BY b.id DESC LIMIT 10000000000000000000 - ) w - GROUP BY w.id`); + ) w`); + + stmt.merge(conn.makeSuffix(filter.fields)); + stmt.merge(conn.makeWhere(filter.where)); + stmt.merge(conn.makeGroupBy('w.id')); + stmt.merge(conn.makeOrderBy(filter.order)); + stmt.merge(conn.makeLimit(filter.limit)); - stmt.merge(conn.makeSuffix(filter)); return conn.executeStmt(stmt); }; From 38c93e8c769ad79387b6a0e30bb92e036a7ffb7a Mon Sep 17 00:00:00 2001 From: Javier Segarra Date: Wed, 17 Jan 2024 14:49:35 +0100 Subject: [PATCH 7/9] refs #6694 test: add test --- back/methods/postcode/filter.js | 67 +++++++++++--- back/methods/postcode/specs/filter.spec.js | 103 +++++++++++++++++++++ 2 files changed, 158 insertions(+), 12 deletions(-) create mode 100644 back/methods/postcode/specs/filter.spec.js diff --git a/back/methods/postcode/filter.js b/back/methods/postcode/filter.js index 0c97c61c5..dcf11f80b 100644 --- a/back/methods/postcode/filter.js +++ b/back/methods/postcode/filter.js @@ -1,3 +1,6 @@ +const {ParameterizedSQL} = require('loopback-connector'); +const {buildFilter, mergeFilters} = require('vn-loopback/util/filter'); +// const {models} = require('vn-loopback/server/server'); module.exports = Self => { Self.remoteMethod('filter', { @@ -8,27 +11,67 @@ module.exports = Self => { type: ['object'], root: true, }, + accepts: [ + { + arg: 'filter', + type: 'object', + description: 'Filter defining where, order, offset, and limit - must be a JSON-encoded string', + http: {source: 'query'} + }, + { + arg: 'search', + type: 'string', + description: 'Value to filter', + http: {source: 'query'} + }, + ], http: { path: `/filter`, verb: 'GET', }, }); Self.filter = async(ctx, filter, options) => { - const {Postcode} = Self.app.models; - const {value} = ctx.where; - const limit = ctx.where?.limit ?? 30; - return await Postcode.rawSql(` - SELECT pc.code, t.name as town, p.name as province , c.country - FROM postCode pc + const myOptions = {}; + + if (typeof options == 'object') + Object.assign(myOptions, options); + + const conn = Self.dataSource.connector; + const where = buildFilter(ctx.args, (param, value) => { + switch (param) { + case 'search': + return {or: [ + {'pc.code': {like: `%${value}%`}}, + {'t.name': {like: `%${value}%`}}, + {'p.name': {like: `%${value}%`}}, + {'c.country': {like: `%${value}%`}} + ] + }; + } + }) ?? {}; + + filter = mergeFilters(ctx.args.filter, {where}); + + const stmts = []; + let stmt; + stmt = new ParameterizedSQL(` + SELECT + pc.code, + t.name as town, + p.name as province, + c.country + FROM + postCode pc JOIN town t on t.id = pc.townFk JOIN province p on p.id = t.provinceFk JOIN country c on c.id = p.countryFk - WHERE - pc.code like '%${value}%' - or t.name like '%${value}%' - or p.name like '%${value}%' - or c.country like '%${value}%' - LIMIT ${limit} `); + + stmt.merge(conn.makeSuffix(filter)); + const itemsIndex = stmts.push(stmt) - 1; + + const sql = ParameterizedSQL.join(stmts, ';'); + const result = await conn.executeStmt(sql, myOptions); + return itemsIndex === 0 ? result : result[itemsIndex]; }; }; diff --git a/back/methods/postcode/specs/filter.spec.js b/back/methods/postcode/specs/filter.spec.js new file mode 100644 index 000000000..c393b629a --- /dev/null +++ b/back/methods/postcode/specs/filter.spec.js @@ -0,0 +1,103 @@ +const {models} = require('vn-loopback/server/server'); + +describe('Postcode filter()', () => { + it('should retrieve with no filter', async() => { + const tx = await models.Postcode.beginTransaction({}); + const options = {transaction: tx}; + + try { + const ctx = { + args: { + + }, + }; + const results = await models.Postcode.filter(ctx, options); + + expect(results.length).toBeGreaterThan(0); + await tx.rollback(); + } catch (e) { + await tx.rollback(); + throw e; + } + }); + + it('should retrieve with filter as postcode', async() => { + const tx = await models.Postcode.beginTransaction({}); + const options = {transaction: tx}; + + try { + const ctx = { + args: { + search: 46, + }, + }; + const results = await models.Postcode.filter(ctx, options); + + expect(results.length).toEqual(4); + await tx.rollback(); + } catch (e) { + await tx.rollback(); + throw e; + } + }); + + it('should retrieve with filter as city', async() => { + const tx = await models.Postcode.beginTransaction({}); + const options = {transaction: tx}; + + try { + const ctx = { + args: { + search: 'Alz', + }, + }; + const results = await models.Postcode.filter(ctx, options); + + expect(results.length).toEqual(1); + await tx.rollback(); + } catch (e) { + await tx.rollback(); + throw e; + } + }); + + it('should retrieve with filter as province', async() => { + const tx = await models.Postcode.beginTransaction({}); + const options = {transaction: tx}; + + try { + const ctx = { + args: { + search: 'one', + }, + }; + const results = await models.Postcode.filter(ctx, options); + + expect(results.length).toEqual(4); + await tx.rollback(); + } catch (e) { + await tx.rollback(); + throw e; + } + }); + + it('should retrieve with filter as country', async() => { + const tx = await models.Postcode.beginTransaction({}); + const options = {transaction: tx}; + + try { + const ctx = { + args: { + search: 'Ec', + }, + }; + const results = await models.Postcode.filter(ctx, options); + + expect(results.length).toEqual(1); + await tx.rollback(); + } catch (e) { + await tx.rollback(); + throw e; + } + }); +}); From 145e72022a76e4686f711c3f7b4915218e849707 Mon Sep 17 00:00:00 2001 From: Javier Segarra Date: Wed, 17 Jan 2024 14:58:55 +0100 Subject: [PATCH 8/9] refs #6694 perf: handle minor errors --- back/methods/postcode/filter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/back/methods/postcode/filter.js b/back/methods/postcode/filter.js index dcf11f80b..9986a16c9 100644 --- a/back/methods/postcode/filter.js +++ b/back/methods/postcode/filter.js @@ -50,7 +50,7 @@ module.exports = Self => { } }) ?? {}; - filter = mergeFilters(ctx.args.filter, {where}); + filter = mergeFilters(ctx.args?.filter ?? {}, {where}); const stmts = []; let stmt; From 0f104e54103580f8b879bbfef21227496533c397 Mon Sep 17 00:00:00 2001 From: carlossa Date: Fri, 19 Jan 2024 10:11:33 +0100 Subject: [PATCH 9/9] refs #6667 view workerDepartment --- modules/worker/back/methods/worker/search.js | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/modules/worker/back/methods/worker/search.js b/modules/worker/back/methods/worker/search.js index 59348a9ce..07bdfe240 100644 --- a/modules/worker/back/methods/worker/search.js +++ b/modules/worker/back/methods/worker/search.js @@ -43,19 +43,13 @@ module.exports = Self => { const stmt = new ParameterizedSQL(` SELECT * FROM( - SELECT w.id, w.code, u.name, u.nickname, u.active, b.departmentFk + SELECT w.id, w.code, u.name, u.nickname, u.active, wd.departmentFk FROM worker w JOIN account.user u ON u.id = w.id - LEFT JOIN business b ON b.workerFk = w.id - ORDER BY b.id DESC - LIMIT 10000000000000000000 + LEFT JOIN workerDepartment wd ON wd.workerFk = w.id ) w`); - stmt.merge(conn.makeSuffix(filter.fields)); - stmt.merge(conn.makeWhere(filter.where)); - stmt.merge(conn.makeGroupBy('w.id')); - stmt.merge(conn.makeOrderBy(filter.order)); - stmt.merge(conn.makeLimit(filter.limit)); + stmt.merge(conn.makeSuffix(filter)); return conn.executeStmt(stmt); };