From 8aa051f0bdb35c85209c274742ab31b9d64345eb Mon Sep 17 00:00:00 2001 From: VLE2FE Date: Fri, 26 Jun 2020 15:23:29 +0200 Subject: [PATCH] switched to aggregation, included material sort keys --- src/routes/sample.spec.ts | 14 +++++++++ src/routes/sample.ts | 54 +++++++++++++++++++---------------- src/routes/validate/sample.ts | 2 +- 3 files changed, 44 insertions(+), 26 deletions(-) diff --git a/src/routes/sample.spec.ts b/src/routes/sample.spec.ts index 5a32356..0aa1c1f 100644 --- a/src/routes/sample.spec.ts +++ b/src/routes/sample.spec.ts @@ -230,6 +230,20 @@ describe('/sample', () => { done(); }); }); + it('sorts the samples correctly for material keys', done => { + TestHelper.request(server, done, { + method: 'get', + url: '/samples?status=all&sort=material.name-desc', + auth: {basic: 'janedoe'}, + httpStatus: 200 + }).end((err, res) => { + if (err) return done(err); + should(res.body[0]).have.property('_id', '400000000000000000000002'); + should(res.body[1]).have.property('_id', '400000000000000000000006'); + should(res.body[2]).have.property('_id', '400000000000000000000001'); + done(); + }); + }); it('rejects a negative page size', done => { TestHelper.request(server, done, { method: 'get', diff --git a/src/routes/sample.ts b/src/routes/sample.ts index 7bc1b67..4a04fc3 100644 --- a/src/routes/sample.ts +++ b/src/routes/sample.ts @@ -25,65 +25,69 @@ router.get('/samples', async (req, res, next) => { const {error, value: filters} = SampleValidate.query(req.query); if (error) return res400(error, res); - let status; + const query = []; + query.push({$match: {$and: []}}); if (filters.hasOwnProperty('status')) { if(filters.status === 'all') { - status = {$or: [{status: globals.status.validated}, {status: globals.status.new}]} + query[0].$match.$and.push({$or: [{status: globals.status.validated}, {status: globals.status.new}]}); } else { - status = {status: globals.status[filters.status]}; + query[0].$match.$and.push({status: globals.status[filters.status]}); } } else { // default - status = {status: globals.status.validated}; + query[0].$match.$and.push({status: globals.status.validated}); } - - const sort = []; - let paging = {} - // sorting filters.sort = filters.sort.split('-'); filters.sort[0] = filters.sort[0] === 'added' ? '_id' : filters.sort[0]; // route added sorting criteria to _id filters.sort[1] = filters.sort[1] === 'desc' ? -1 : 1; - if (!filters['to-page']) { // set to-page default filters['to-page'] = 0; } + if (filters.sort[0].indexOf('material.') >= 0) { // need to populate materials, material supplier and group + query.push( + {$lookup: {from: 'materials', localField: 'material_id', foreignField: '_id', as: 'material'}}, + {$set: {material: { $arrayElemAt: ['$material', 0]}}}, + {$lookup: { from: 'material_groups', localField: 'material.group_id', foreignField: '_id', as: 'material.group' }}, + {$set: {'material.group': { $arrayElemAt: ['$material.group.name', 0]}}}, + {$lookup: { from: 'material_suppliers', localField: 'material.supplier_id', foreignField: '_id', as: 'material.supplier'}}, + {$set: {'material.supplier': {$arrayElemAt: ['$material.supplier.name', 0]}}} + ); + } + if (filters['from-id']) { // from-id specified - const fromSample = SampleValidate.output(await SampleModel.findById(filters['from-id']).lean().exec().catch(err => {next(err);})); + const fromSample = await SampleModel.findById(filters['from-id']).lean().exec().catch(err => {next(err);}); if (fromSample instanceof Error) return; if ((filters['to-page'] === 0 && filters.sort[1] === 1) || (filters.sort[1] * filters['to-page'] > 0)) { // asc - paging = {$or: [{[filters.sort[0]]: {$gt: fromSample[filters.sort[0]]}}, {$and: [{[filters.sort[0]]: fromSample[filters.sort[0]]}, {_id: {$gte: mongoose.Types.ObjectId(filters['from-id'])}}]}]}; - sort.push([filters.sort[0], 1]); - sort.push(['_id', 1]); + query[0].$match.$and.push({$or: [{[filters.sort[0]]: {$gt: fromSample[filters.sort[0]]}}, {$and: [{[filters.sort[0]]: fromSample[filters.sort[0]]}, {_id: {$gte: new mongoose.Types.ObjectId(filters['from-id'])}}]}]}); + query.push({$sort: {[filters.sort[0]]: 1, _id: 1}}); } else { - paging = {$or: [{[filters.sort[0]]: {$lt: fromSample[filters.sort[0]]}}, {$and: [{[filters.sort[0]]: fromSample[filters.sort[0]]}, {_id: {$lte: mongoose.Types.ObjectId(filters['from-id'])}}]}]}; - sort.push([filters.sort[0], -1]); - sort.push(['_id', -1]); + query[0].$match.$and.push({$or: [{[filters.sort[0]]: {$lt: fromSample[filters.sort[0]]}}, {$and: [{[filters.sort[0]]: fromSample[filters.sort[0]]}, {_id: {$lte: new mongoose.Types.ObjectId(filters['from-id'])}}]}]}); + query.push({$sort: {[filters.sort[0]]: -1, _id: -1}}); } } else { // sort from beginning - sort.push([filters.sort[0], filters.sort[1]]); // set _id as secondary sort - sort.push(['_id', filters.sort[1]]); // set _id as secondary sort + query.push({$sort: {[filters.sort[0]]: filters.sort[1], '_id': filters.sort[1]}}); // set _id as secondary sort } - const query = SampleModel.find({$and: [status, paging]}); - - if (filters['page-size']) { - query.limit(filters['page-size']); + if (filters.sort[0].indexOf('material.') >= 0) { // unpopulate materials again + query.push({$unset: 'material'}); } if (filters['to-page']) { - query.skip(Math.abs(filters['to-page'] + Number(filters['to-page'] < 0)) * filters['page-size'] + Number(filters['to-page'] < 0)); + query.push({$skip: Math.abs(filters['to-page'] + Number(filters['to-page'] < 0)) * filters['page-size'] + Number(filters['to-page'] < 0)}) // number to skip, if going back pages, one page has to be skipped less but on sample more } - query.sort(sort); + if (filters['page-size']) { + query.push({$limit: filters['page-size']}); + } - query.lean().exec((err, data) => { + SampleModel.aggregate(query).exec((err, data) => { if (err) return next(err); if (filters['to-page'] < 0) { data.reverse(); diff --git a/src/routes/validate/sample.ts b/src/routes/validate/sample.ts index 7a706c2..b55b847 100644 --- a/src/routes/validate/sample.ts +++ b/src/routes/validate/sample.ts @@ -133,7 +133,7 @@ export default class SampleValidate { 'from-id': IdValidate.get(), 'to-page': Joi.number().integer(), 'page-size': Joi.number().integer().min(1), - sort: Joi.string().pattern(/^(_id|color|number|type|batch|added)-(asc|desc)$/m).default('_id-asc') // TODO: material keys + sort: Joi.string().pattern(/^(_id|color|number|type|batch|added|material\.name|material\.supplier|material\.group|material\.mineral|material\.glass_fiber|material\.carbon_fiber)-(asc|desc)$/m).default('_id-asc') // TODO: material keys }).with('to-page', 'page-size').validate(data); } } \ No newline at end of file