sorting for direct sample properties added
This commit is contained in:
parent
49f7a475b7
commit
43413001e9
@ -30,6 +30,12 @@
|
|||||||
schema:
|
schema:
|
||||||
type: string
|
type: string
|
||||||
example: 30
|
example: 30
|
||||||
|
- name: sort
|
||||||
|
description: sorting of results, in format 'key-asc/desc'
|
||||||
|
in: query
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
example: color-asc
|
||||||
responses:
|
responses:
|
||||||
200:
|
200:
|
||||||
description: samples overview
|
description: samples overview
|
||||||
|
@ -177,6 +177,59 @@ describe('/sample', () => {
|
|||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
it('sorts the samples ascending', done => {
|
||||||
|
TestHelper.request(server, done, {
|
||||||
|
method: 'get',
|
||||||
|
url: '/samples?status=all&sort=color-asc',
|
||||||
|
auth: {basic: 'janedoe'},
|
||||||
|
httpStatus: 200
|
||||||
|
}).end((err, res) => {
|
||||||
|
if (err) return done(err);
|
||||||
|
should(res.body[0]).have.property('color', 'black');
|
||||||
|
should(res.body[res.body.length - 1]).have.property('color', 'natural');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('sorts the samples descending', done => {
|
||||||
|
TestHelper.request(server, done, {
|
||||||
|
method: 'get',
|
||||||
|
url: '/samples?status=all&sort=number-desc',
|
||||||
|
auth: {basic: 'janedoe'},
|
||||||
|
httpStatus: 200
|
||||||
|
}).end((err, res) => {
|
||||||
|
if (err) return done(err);
|
||||||
|
should(res.body[0]).have.property('number', 'Rng36');
|
||||||
|
should(res.body[1]).have.property('number', '33');
|
||||||
|
should(res.body[res.body.length - 1]).have.property('number', '1');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('sorts the samples correctly in combination with paging', done => {
|
||||||
|
TestHelper.request(server, done, {
|
||||||
|
method: 'get',
|
||||||
|
url: '/samples?status=all&sort=color-asc&page-size=2&from-id=400000000000000000000006',
|
||||||
|
auth: {basic: 'janedoe'},
|
||||||
|
httpStatus: 200
|
||||||
|
}).end((err, res) => {
|
||||||
|
if (err) return done(err);
|
||||||
|
should(res.body[0]).have.property('_id', '400000000000000000000006');
|
||||||
|
should(res.body[1]).have.property('_id', '400000000000000000000002');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('sorts the samples correctly in combination with going pages backward', done => {
|
||||||
|
TestHelper.request(server, done, {
|
||||||
|
method: 'get',
|
||||||
|
url: '/samples?status=all&sort=color-desc&page-size=2&from-id=400000000000000000000004&to-page=-1',
|
||||||
|
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');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
it('rejects a negative page size', done => {
|
it('rejects a negative page size', done => {
|
||||||
TestHelper.request(server, done, {
|
TestHelper.request(server, done, {
|
||||||
method: 'get',
|
method: 'get',
|
||||||
|
@ -19,7 +19,7 @@ import db from '../db';
|
|||||||
|
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
||||||
router.get('/samples', (req, res, next) => {
|
router.get('/samples', async (req, res, next) => {
|
||||||
if (!req.auth(res, ['read', 'write', 'maintain', 'dev', 'admin'], 'all')) return;
|
if (!req.auth(res, ['read', 'write', 'maintain', 'dev', 'admin'], 'all')) return;
|
||||||
|
|
||||||
const {error, value: filters} = SampleValidate.query(req.query);
|
const {error, value: filters} = SampleValidate.query(req.query);
|
||||||
@ -37,29 +37,55 @@ router.get('/samples', (req, res, next) => {
|
|||||||
else { // default
|
else { // default
|
||||||
status = {status: globals.status.validated};
|
status = {status: globals.status.validated};
|
||||||
}
|
}
|
||||||
const query = SampleModel.find(status);
|
|
||||||
|
|
||||||
|
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['from-id']) { // from-id specified
|
||||||
|
const fromSample = SampleValidate.output(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]);
|
||||||
|
}
|
||||||
|
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]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
const query = SampleModel.find({$and: [status, paging]});
|
||||||
|
|
||||||
if (filters['page-size']) {
|
if (filters['page-size']) {
|
||||||
query.limit(filters['page-size']);
|
query.limit(filters['page-size']);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (filters['from-id']) {
|
if (filters['to-page']) {
|
||||||
if (filters['to-page'] && filters['to-page'] < 0) {
|
query.skip(Math.abs(filters['to-page'] + Number(filters['to-page'] < 0)) * filters['page-size'] + Number(filters['to-page'] < 0));
|
||||||
query.lt('_id', mongoose.Types.ObjectId(filters['from-id'])); // TODO: consider sorting
|
|
||||||
query.sort({_id: -1});
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
query.gte('_id', mongoose.Types.ObjectId(filters['from-id'])); // TODO: consider sorting
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (filters['to-page']) {
|
query.sort(sort);
|
||||||
query.skip(Math.abs(filters['to-page'] + Number(filters['to-page'] < 0)) * filters['page-size']); // TODO: check order for negative numbers
|
|
||||||
}
|
|
||||||
|
|
||||||
query.lean().exec((err, data) => {
|
query.lean().exec((err, data) => {
|
||||||
if (err) return next(err);
|
if (err) return next(err);
|
||||||
if (filters['to-page'] && filters['to-page'] < 0) {
|
if (filters['to-page'] < 0) {
|
||||||
data.reverse();
|
data.reverse();
|
||||||
}
|
}
|
||||||
res.json(_.compact(data.map(e => SampleValidate.output(e)))); // validate all and filter null values from validation errors
|
res.json(_.compact(data.map(e => SampleValidate.output(e)))); // validate all and filter null values from validation errors
|
||||||
|
@ -132,7 +132,8 @@ export default class SampleValidate {
|
|||||||
status: Joi.string().valid('validated', 'new', 'all'),
|
status: Joi.string().valid('validated', 'new', 'all'),
|
||||||
'from-id': IdValidate.get(),
|
'from-id': IdValidate.get(),
|
||||||
'to-page': Joi.number().integer(),
|
'to-page': Joi.number().integer(),
|
||||||
'page-size': Joi.number().integer().min(1)
|
'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
|
||||||
}).with('to-page', 'page-size').validate(data);
|
}).with('to-page', 'page-size').validate(data);
|
||||||
}
|
}
|
||||||
}
|
}
|
Reference in New Issue
Block a user