Archived
2

added filters

This commit is contained in:
VLE2FE
2020-07-06 09:43:04 +02:00
parent e4bc5a77f1
commit 29eefce0c9
6 changed files with 176 additions and 17 deletions

View File

@ -44,7 +44,7 @@ router.get('/samples', async (req, res, next) => {
if (filters.sort[0].indexOf('measurements.') >= 0) { // sorting with measurements as starting collection
collection = MeasurementModel;
const measurementName = filters.sort[0].replace('measurements.', '');
const [,measurementName, measurementParam] = filters.sort[0].split('.');
const measurementTemplate = await MeasurementTemplateModel.findOne({name: measurementName}).lean().exec().catch(err => {next(err);});
if (measurementTemplate instanceof Error) return;
if (!measurementTemplate) {
@ -57,11 +57,14 @@ router.get('/samples', async (req, res, next) => {
if (!fromSample) {
return res.status(400).json({status: 'Invalid body format', details: 'from-id not found'});
}
sortStartValue = fromSample.values[measurementTemplate.parameters[0].name];
sortStartValue = fromSample.values[measurementParam];
}
query[0].$match.$and.push({measurement_template: mongoose.Types.ObjectId(measurementTemplate._id)}); // find measurements to sort
if (filters.filters.find(e => e.field === filters.sort[0])) { // sorted measurement should also be filtered
query[0].$match.$and.push(...filterQueries(filters.filters.find(e => e.field === filters.sort[0])));
}
query.push(
sortQuery(query, filters, ['values.' + measurementTemplate.parameters[0].name, 'sample_id'], sortStartValue), // sort measurements
sortQuery(query, filters, ['values.' + measurementParam, 'sample_id'], sortStartValue), // sort measurements
{$replaceRoot: {newRoot: {measurement: '$$ROOT'}}}, // fetch samples and restructure them to fit sample structure
{$lookup: {from: 'samples', localField: 'measurement.sample_id', foreignField: '_id', as: 'sample'}},
{$match: statusQuery(filters, 'sample.status')} // filter out wrong status once samples were added
@ -71,12 +74,13 @@ router.get('/samples', async (req, res, next) => {
{$set: {['sample.' + measurementName]: '$measurement.values'}}, // more restructuring
{$replaceRoot: {newRoot: {$mergeObjects: [{$arrayElemAt: ['$sample', 0]}, {}]}}}
);
addFilterQueries(query, filters.filters.filter(e => sampleKeys.indexOf(e.field) >= 0)); // sample filters
}
else { // sorting with samples as starting collection
collection = SampleModel;
// filter for status
query[0].$match.$and.push(statusQuery(filters, 'status'));
addFilterQueries(query, filters.filters.filter(e => sampleKeys.indexOf(e.field) >= 0)); // sample filters
// differentiate by sort key to do sorting, skip and limit as early as possible
if (sampleKeys.indexOf(filters.sort[0]) >= 0) { // sorting for sample keys
@ -90,6 +94,7 @@ router.get('/samples', async (req, res, next) => {
sortStartValue = fromSample[filters.sort[0]];
}
query.push(sortQuery(query, filters, [filters.sort[0], '_id'], sortStartValue));
// material filters
addSkipLimit(query, filters);
}
else { // sorting for material keys
@ -130,41 +135,54 @@ router.get('/samples', async (req, res, next) => {
}
}
const fieldsNoSort = filters.fields.filter(e => e !== filters.sort[0]); // sort field was definitely added already, exclude from further field operations
if (fieldsNoSort.find(e => /material\./.test(e))) { // add material fields
const fieldsToAdd = [
...filters.fields,
...filters.filters.map(e => e.field) // add filter fields in case they were not specified to display
].filter(e => e !== filters.sort[0]) // sort field was definitely added already, exclude from further field operations
.filter((e, i, self) => self.indexOf(e) === i); // remove duplicates
if (fieldsToAdd.find(e => /material\./.test(e))) { // add material fields
query.push(
{$lookup: {from: 'materials', localField: 'material_id', foreignField: '_id', as: 'material'}},
{$set: {material: { $arrayElemAt: ['$material', 0]}}}
);
}
if (fieldsNoSort.indexOf('material.supplier') >= 0) { // add supplier if needed
if (fieldsToAdd.indexOf('material.supplier') >= 0) { // add supplier if needed
query.push(
{$lookup: { from: 'material_suppliers', localField: 'material.supplier_id', foreignField: '_id', as: 'material.supplier'}},
{$set: {'material.supplier': {$arrayElemAt: ['$material.supplier.name', 0]}}}
);
}
if (fieldsNoSort.indexOf('material.group') >= 0) { // add group if needed
if (fieldsToAdd.indexOf('material.group') >= 0) { // add group if needed
query.push(
{$lookup: { from: 'material_groups', localField: 'material.group_id', foreignField: '_id', as: 'material.group' }},
{$set: {'material.group': { $arrayElemAt: ['$material.group.name', 0]}}}
);
}
if (fieldsNoSort.indexOf('material.number') >= 0) { // add material number if needed
if (fieldsToAdd.indexOf('material.number') >= 0) { // add material number if needed
query.push(
{$set: {'material.number': { $arrayElemAt: ['$material.numbers.number', {$indexOfArray: ['$material.numbers.color', '$color']}]}}}
);
}
let measurementFields = filters.fields.filter(e => /measurements\./.test(e)).map(e => e.replace('measurements.', ''));
console.log(fieldsNoSort);
console.log(measurementFields);
if (fieldsNoSort.find(e => /measurements\./.test(e))) { // add measurement fields
query.push({$lookup: {from: 'measurements', localField: '_id', foreignField: 'sample_id', as: 'measurements'}});
addFilterQueries(query, filters.filters.filter(e => /material\./.test(e.field))); // material filters
let measurementFields = fieldsToAdd.filter(e => /measurements\./.test(e)).map(e => e.split('.')[1]).filter((e, i, self) => self.indexOf(e) === i); // filter measurement names and remove duplicates from parameters
if (fieldsToAdd.find(e => /measurements\./.test(e))) { // add measurement fields
const measurementTemplates = await MeasurementTemplateModel.find({$or: measurementFields.filter(e => e !== filters.sort[0].replace('measurements.', '')).map(e => {return {name: e}})}).lean().exec().catch(err => {next(err);});
if (measurementTemplates instanceof Error) return;
if (measurementTemplates.length < measurementFields.length) {
return res.status(400).json({status: 'Invalid body format', details: 'Measurement key not found'});
}
if (fieldsToAdd.find(e => e === 'measurements.spectrum')) { // use different lookup methods with and without spectrum for the best performance
query.push({$lookup: {from: 'measurements', localField: '_id', foreignField: 'sample_id', as: 'measurements'}});
}
else {
query.push({$lookup: {
from: 'measurements', let: {sId: '$_id'},
pipeline: [{$match: {$expr: {$and: [{$eq: ['$sample_id', '$$sId']}, {$in: ['$measurement_template', measurementTemplates.map(e => mongoose.Types.ObjectId(e._id))]}]}}}],
as: 'measurements'
}});
}
measurementTemplates.filter(e => e.name !== 'spectrum').forEach(template => { // TODO: hard coded dpt for special treatment, change later
query.push({$set: {[template.name]: {$let: { // add measurements as property [template.name], if one result, array is reduced to direct values
vars: {arr: {$filter: {input: '$measurements', cond: {$eq: ['$$this.measurement_template', mongoose.Types.ObjectId(template._id)]}}}},
@ -180,6 +198,7 @@ router.get('/samples', async (req, res, next) => {
}
query.push({$unset: 'measurements'});
}
addFilterQueries(query, filters.filters.filter(e => /measurements\./.test(e.field)).map(e => {e.field = e.field.replace('measurements.', ''); return e; })); // measurement filters
const projection = filters.fields.map(e => e.replace('measurements.', '')).reduce((s, e) => {s[e] = true; return s; }, {});
if (filters.fields.indexOf('added') >= 0) { // add added date
@ -601,4 +620,14 @@ function statusQuery(filters, field) {
else { // default
return {[field]: globals.status.validated};
}
}
function addFilterQueries (query, filters) { // returns array of match queries from given filters
if (filters.length) {
query.push({$match: {$and: filterQueries(filters)}});
}
}
function filterQueries (filters) {
return filters.map(e => ({[e.field]: {['$' + e.mode]: (e.mode.indexOf('in') >= 0 ? e.values : e.values[0])}})) // add filter criteria as {field: {$mode: value}}, only use first value when mode is not in/nin
}