| 
						
					 | 
				
			
			 | 
			 | 
			
				@@ -21,6 +21,12 @@ import csv from '../helpers/csv';
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				const router = express.Router();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				// TODO: check added filter
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				// TODO: return total number of pages -> use facet
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				// TODO: use query pointer
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				// TODO: convert filter value to number according to table model
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				// TODO: validation for filter parameters
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				// TODO: location/device sort/filter
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				router.get('/samples', async (req, res, next) => {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  if (!req.auth(res, ['read', 'write', 'maintain', 'dev', 'admin'], 'all')) return;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
	
		
			
				
					
					| 
						
					 | 
				
			
			 | 
			 | 
			
				@@ -37,6 +43,7 @@ router.get('/samples', async (req, res, next) => {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  if (!filters['to-page']) {  // set to-page default
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    filters['to-page'] = 0;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  console.log(filters);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  const sortFilterKeys = filters.filters.map(e => e.field);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
	
		
			
				
					
					| 
						
					 | 
				
			
			 | 
			 | 
			
				@@ -70,7 +77,7 @@ router.get('/samples', async (req, res, next) => {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      {$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
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      {$set: {['sample.' + measurementName]: '$measurement.values'}},  // more restructuring
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      {$addFields: {['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
 | 
			
		
		
	
	
		
			
				
					
					| 
						
					 | 
				
			
			 | 
			 | 
			
				@@ -106,25 +113,25 @@ router.get('/samples', async (req, res, next) => {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    materialAdded = true;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    materialQuery.push(  // add material properties
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      {$lookup: {from: 'materials', localField: 'material_id', foreignField: '_id', as: 'material'}},  // TODO: project out unnecessary fields
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      {$set: {material: {$arrayElemAt: ['$material', 0]}}}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      {$addFields: {material: {$arrayElemAt: ['$material', 0]}}}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    );
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    const baseMFilters = sortFilterKeys.filter(e => /material\./.test(e)).filter(e => ['material.supplier', 'material.group', 'material.number'].indexOf(e) < 0);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    addFilterQueries(materialQuery, filters.filters.filter(e => baseMFilters.indexOf(e.field) >= 0));  // base material filters
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    if (sortFilterKeys.find(e => e === 'material.supplier')) {  // add supplier if needed
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      materialQuery.push(
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        {$lookup: { from: 'material_suppliers', localField: 'material.supplier_id', foreignField: '_id', as: 'material.supplier'}},
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        {$set: {'material.supplier': {$arrayElemAt: ['$material.supplier.name', 0]}}}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        {$addFields: {'material.supplier': {$arrayElemAt: ['$material.supplier.name', 0]}}}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      );
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    if (sortFilterKeys.find(e => e === 'material.group')) {  // add group if needed
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      materialQuery.push(
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        {$lookup: { from: 'material_groups', localField: 'material.group_id', foreignField: '_id', as: 'material.group' }},
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        {$set: {'material.group': { $arrayElemAt: ['$material.group.name', 0]}}}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        {$addFields: {'material.group': { $arrayElemAt: ['$material.group.name', 0]}}}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      );
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    if (sortFilterKeys.find(e => e === 'material.number')) {  // add material number if needed
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      materialQuery.push(
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        {$set: {'material.number': { $arrayElemAt: ['$material.numbers.number', {$indexOfArray: ['$material.numbers.color', '$color']}]}}}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        {$addFields: {'material.number': { $arrayElemAt: ['$material.numbers.number', {$indexOfArray: ['$material.numbers.color', '$color']}]}}}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      );
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    const specialMFilters = sortFilterKeys.filter(e => /material\./.test(e)).filter(e => ['material.supplier', 'material.group', 'material.number'].indexOf(e) >= 0);
 | 
			
		
		
	
	
		
			
				
					
					| 
						
					 | 
				
			
			 | 
			 | 
			
				@@ -157,10 +164,10 @@ router.get('/samples', async (req, res, next) => {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        as: 'measurements'
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }});
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    measurementTemplates.forEach(template => {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      query.push({$set: {[template.name]: {$let: {  // add measurements as property [template.name], if one result, array is reduced to direct values
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      query.push({$addFields: {[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)]}}}},
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        in:{$cond: [{$lte: [{$size: '$$arr'}, 1]}, {$arrayElemAt: ['$$arr', 0]}, '$$arr']}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      }}}}, {$set: {[template.name]: {$cond: ['$' + template.name + '.values', '$' + template.name + '.values', template.parameters.reduce((s, e) => {s[e.name] = null; return s;}, {})]}}});
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      }}}}, {$addFields: {[template.name]: {$cond: ['$' + template.name + '.values', '$' + template.name + '.values', template.parameters.reduce((s, e) => {s[e.name] = null; return s;}, {})]}}});
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    });
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    addFilterQueries(query, filters.filters
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      .filter(e => sortFilterKeys.filter(e => /measurements\./.test(e)).indexOf(e.field) >= 0)
 | 
			
		
		
	
	
		
			
				
					
					| 
						
					 | 
				
			
			 | 
			 | 
			
				@@ -173,39 +180,40 @@ router.get('/samples', async (req, res, next) => {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    sortFilterKeys.indexOf(e) < 0                      // field was not in filter
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    && e !== filters.sort[0]                           // field was not in sort
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  );
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  console.log(fieldsToAdd);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  if (fieldsToAdd.find(e => /material\./.test(e)) && !materialAdded) {  // add material, was not added already
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    query.push(
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      {$lookup: {from: 'materials', localField: 'material_id', foreignField: '_id', as: 'material'}},
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      {$set: {material: { $arrayElemAt: ['$material', 0]}}}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      {$addFields: {material: { $arrayElemAt: ['$material', 0]}}}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    );
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  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]}}}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      {$addFields: {'material.supplier': {$arrayElemAt: ['$material.supplier.name', 0]}}}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    );
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  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]}}}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      {$addFields: {'material.group': { $arrayElemAt: ['$material.group.name', 0]}}}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    );
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  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']}]}}}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      {$addFields: {'material.number': { $arrayElemAt: ['$material.numbers.number', {$indexOfArray: ['$material.numbers.color', '$color']}]}}}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    );
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  let measurementFieldsFields = _.uniq(fieldsToAdd.filter(e => /measurements\./.test(e)).map(e => e.split('.')[1]));  // filter measurement names and remove duplicates from parameters
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  let measurementFieldsFields: string[] = _.uniq(fieldsToAdd.filter(e => /measurements\./.test(e)).map(e => e.split('.')[1]));  // filter measurement names and remove duplicates from parameters
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  if (fieldsToAdd.find(e => /measurements\./.test(e))) {  // add measurement fields
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    const measurementTemplates = await MeasurementTemplateModel.find({name: {$in: measurementFieldsFields}}).lean().exec().catch(err => {next(err);});
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    if (measurementTemplates instanceof Error) return;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    if (measurementTemplates.length < measurementFieldsFields.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
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    if (fieldsToAdd.find(e => /spectrum\./.test(e))) {  // 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 {
 | 
			
		
		
	
	
		
			
				
					
					| 
						
					 | 
				
			
			 | 
			 | 
			
				@@ -216,15 +224,15 @@ router.get('/samples', async (req, res, next) => {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        }});
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    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
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      query.push({$addFields: {[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)]}}}},
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				              in:{$cond: [{$lte: [{$size: '$$arr'}, 1]}, {$arrayElemAt: ['$$arr', 0]}, '$$arr']}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            }}}}, {$set: {[template.name]: {$cond: ['$' + template.name + '.values', '$' + template.name + '.values', template.parameters.reduce((s, e) => {s[e.name] = null; return s;}, {})]}}});
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            }}}}, {$addFields: {[template.name]: {$cond: ['$' + template.name + '.values', '$' + template.name + '.values', template.parameters.reduce((s, e) => {s[e.name] = null; return s;}, {})]}}});
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    });
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    if (measurementFieldsFields.find(e => e === 'spectrum')) {  // TODO: remove hardcoded as well
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      query.push(
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        {$set: {spectrum: {$filter: {input: '$measurements', cond: {$eq: ['$$this.measurement_template', measurementTemplates.filter(e => e.name === 'spectrum')[0]._id]}}}}},
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        {$set: {spectrum: '$spectrum.values.dpt'}},
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        {$addFields: {spectrum: {$filter: {input: '$measurements', cond: {$eq: ['$$this.measurement_template', measurementTemplates.filter(e => e.name === 'spectrum')[0]._id]}}}}},
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        {$addFields: {spectrum: '$spectrum.values'}},
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        {$unwind: '$spectrum'}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      );
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
	
		
			
				
					
					| 
						
					 | 
				
			
			 | 
			 | 
			
				@@ -233,30 +241,45 @@ router.get('/samples', async (req, res, next) => {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  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
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    projection.added = {$toDate: '$_id'};
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    // projection.added = {$toDate: '$_id'};
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    // projection.added = { $convert: { input: '$_id', to: "date" } }  // TODO
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  if (!(filters.fields.indexOf('_id') >= 0)) {  // disable _id explicitly
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    projection._id = false;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  query.push({$project: projection});
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  collection.aggregate(query).exec((err, data) => {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    if (err) return next(err);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    if (filters['to-page'] < 0) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      data.reverse();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    const measurementFields = _.uniq([...measurementFilterFields, ...measurementFieldsFields]);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    if (filters.csv) {  // output as csv
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      csv(_.compact(data.map(e => SampleValidate.output(e, 'refs', measurementFields))), (err, data) => {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        if (err) return next(err);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        res.set('Content-Type', 'text/csv');
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        res.send(data);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      });
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    else {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      res.json(_.compact(data.map(e => SampleValidate.output(e, 'refs', measurementFields))));  // validate all and filter null values from validation errors
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  })
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  if (!fieldsToAdd.find(e => /spectrum\./.test(e))) {  // use streaming when including spectrum files
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    collection.aggregate(query).exec((err, data) => {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      if (err) return next(err);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      console.log(data.length);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      if (filters['to-page'] < 0) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        data.reverse();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      const measurementFields = _.uniq([filters.sort[0].split('.')[1], ...measurementFilterFields, ...measurementFieldsFields]);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      if (filters.csv) {  // output as csv
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        csv(_.compact(data.map(e => SampleValidate.output(e, 'refs', measurementFields))), (err, data) => {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				          if (err) return next(err);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				          res.set('Content-Type', 'text/csv');
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				          res.send(data);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        });
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      else {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        res.json(_.compact(data.map(e => SampleValidate.output(e, 'refs', measurementFields))));  // validate all and filter null values from validation errors
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    });
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  else {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    res.writeHead(200, {'Content-Type': 'application/json; charset=utf-8'});
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    res.write('[');
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    let count = 0;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    const stream = collection.aggregate(query).cursor().exec();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    stream.on('data', data => { res.write((count === 0 ? '' : ',\n') + JSON.stringify(data)); count ++; });
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    stream.on('close', () => {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      res.write(']');
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      res.end();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    });
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				});
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				router.get('/samples/:state(new|deleted)', (req, res, next) => {
 | 
			
		
		
	
	
		
			
				
					
					| 
						
					 | 
				
			
			 | 
			 | 
			
				@@ -537,7 +560,7 @@ async function materialCheck (sample, res, next, id = sample.material_id) {  //
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    res.status(400).json({status: 'Material not available'});
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    return false;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  if (sample.hasOwnProperty('color') && !materialData.numbers.find(e => e.color === sample.color)) {  // color for material not specified
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  if (sample.hasOwnProperty('color') && sample.color !== '' && !materialData.numbers.find(e => e.color === sample.color)) {  // color for material not specified
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    res.status(400).json({status: 'Color not available for material'});
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    return false;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  }
 | 
			
		
		
	
	
		
			
				
					
					| 
						
					 | 
				
			
			 | 
			 | 
			
				 
 |