Skip to content

Commit 17f361e

Browse files
authored
Merge branch 'main' into feature/O2B-1508/lhcfills-beam-types-filter
2 parents 8797eb6 + 7f6dbfd commit 17f361e

4 files changed

Lines changed: 186 additions & 17 deletions

File tree

lib/database/utilities/QueryBuilder.js

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,55 @@ class WhereQueryBuilder {
145145
}
146146

147147
/**
148-
* Set the next filter condition according to a comparison operator ("<", "<=", ">=", ">")
148+
* Set a min range limit using the provided value OR null (useful when 0 is stored as null)
149+
*
150+
* @param {*} min the lower limit criteria
151+
* @param {boolean} strict if true, the comparison is exclusive, else it will be inclusive (equality match)
152+
*
153+
* @return {void}
154+
*/
155+
greaterThanOrNull(min, strict) {
156+
if (this.notFlag) {
157+
this.notFlag = !this.notFlag;
158+
this.lowerThan(min, !strict);
159+
this.notFlag = !this.notFlag;
160+
return;
161+
}
162+
const operation = {
163+
[Op.or]: [
164+
{ [Op.is]: null },
165+
{ [strict ? Op.gt : Op.gte]: min },
166+
],
167+
};
168+
this._op(operation);
169+
}
170+
171+
/**
172+
* Set a max range limit using the provided value OR null (useful when 0 is stored as null)
173+
*
174+
* @param {*} max the upper limit criteria
175+
* @param {boolean} strict if true, the comparison is exclusive, else it will be inclusive (equality match)
176+
*
177+
* @return {void}
178+
*/
179+
lowerThanOrNull(max, strict) {
180+
if (this.notFlag) {
181+
this.notFlag = !this.notFlag;
182+
this.greaterThan(max, !strict);
183+
this.notFlag = !this.notFlag;
184+
return;
185+
}
186+
const operation = {
187+
[Op.or]: [
188+
{ [Op.is]: null },
189+
{ [strict ? Op.lt : Op.lte]: max },
190+
],
191+
};
192+
this._op(operation);
193+
}
194+
195+
/**
196+
* Set the next filter condition according to a comparison operator ("<", "<=", ">=", ">", "=")
149197
*
150198
* @param {"<"|"<="|"="|">="|">"} operator the operator to apply
151199
* @param {*} limit the limit to compare to

lib/usecases/lhcFill/GetAllLhcFillsUseCase.js

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -65,14 +65,13 @@ class GetAllLhcFillsUseCase {
6565
}
6666
}
6767

68-
// Run duration filter and corresponding operator.
6968
if (runDuration?.limit !== undefined && runDuration?.operator) {
7069
associatedStatisticsRequired = true;
7170
// 00:00:00 aka 0 value is saved in the DB as null (bookkeeping.fill_statistics.runs_coverage)
72-
if ((runDuration.operator === '>=' || runDuration.operator === '<=') && Number(runDuration.limit) === 0) {
73-
// Include 00:00:00 = 0 = null AND everything above 00:00:00 which is more or less than 0.
74-
queryBuilder.whereAssociation('statistics', 'runsCoverage').applyOperator(runDuration.operator, 0);
75-
queryBuilder.whereAssociation('statistics', 'runsCoverage').applyOperator('=', null);
71+
if (runDuration.operator === '<=' && Number(runDuration.limit) === 0) {
72+
queryBuilder.whereAssociation('statistics', 'runsCoverage').lowerThanOrNull(0, false);
73+
} else if (runDuration.operator === '>=' && Number(runDuration.limit) === 0) {
74+
queryBuilder.whereAssociation('statistics', 'runsCoverage').greaterThanOrNull(0, false);
7675
} else if ((runDuration.operator === '>' || runDuration.operator === '<') && Number(runDuration.limit) === 0) {
7776
queryBuilder.whereAssociation('statistics', 'runsCoverage').applyOperator(runDuration.operator, 0);
7877
} else if (Number(runDuration.limit) === 0) {
@@ -81,6 +80,7 @@ class GetAllLhcFillsUseCase {
8180
queryBuilder.whereAssociation('statistics', 'runsCoverage').applyOperator(runDuration.operator, runDuration.limit);
8281
}
8382
}
83+
8484
// Beam duration filter, limit and corresponding operator.
8585
if (beamDuration?.limit !== undefined && beamDuration?.operator) {
8686
queryBuilder.where('stableBeamsDuration').applyOperator(beamDuration.operator, beamDuration.limit);
@@ -99,10 +99,13 @@ class GetAllLhcFillsUseCase {
9999
where: { definition: RunDefinition.PHYSICS },
100100
required: false,
101101
});
102-
queryBuilder.include({
103-
association: 'statistics',
104-
required: associatedStatisticsRequired,
105-
});
102+
103+
if (associatedStatisticsRequired) {
104+
queryBuilder.include({
105+
association: 'statistics',
106+
required: true,
107+
});
108+
}
106109

107110
queryBuilder.orderBy('fillNumber', 'desc');
108111
queryBuilder.limit(limit);

test/lib/database/utilities/QueryBuilder.test.js

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
const {
1515
repositories: {
1616
LogRepository,
17+
RunRepository,
1718
},
1819
utilities: {
1920
QueryBuilder,
@@ -208,5 +209,84 @@ module.exports = () => {
208209
});
209210
});
210211
});
212+
213+
// 104 runs with null inelastic_interaction_rate_avg
214+
// 5 runs with inelastic_interaction_rate_avg values: 20000, 40000, 500000, 600000, 755311
215+
216+
describe('greaterThanOrNull', () => {
217+
it('should return entities with inelastic_interaction_rate_avg greater than or equal to 40000 or null', async () => {
218+
const queryBuilder = new QueryBuilder();
219+
queryBuilder.where('inelastic_interaction_rate_avg').greaterThanOrNull(40000);
220+
221+
const result = await RunRepository.findAll(queryBuilder);
222+
expect(result).to.not.be.null;
223+
expect(result).to.have.lengthOf(108);
224+
result.forEach(({ inelasticInteractionRateAvg }) => {
225+
expect(inelasticInteractionRateAvg === null || inelasticInteractionRateAvg >= 40000).to.be.true;
226+
});
227+
});
228+
229+
it('should return entities with inelastic_interaction_rate_avg greater than 40000 or null', async () => {
230+
const queryBuilder = new QueryBuilder();
231+
queryBuilder.where('inelastic_interaction_rate_avg').greaterThanOrNull(40000, true);
232+
233+
const result = await RunRepository.findAll(queryBuilder);
234+
expect(result).to.not.be.null;
235+
expect(result).to.have.lengthOf(107);
236+
result.forEach(({ inelasticInteractionRateAvg }) => {
237+
expect(inelasticInteractionRateAvg === null || inelasticInteractionRateAvg > 40000).to.be.true;
238+
});
239+
});
240+
241+
it('should return entities with not (inelasticInteractionRateAvg >= 40000 or null)', async () => {
242+
const queryBuilder = new QueryBuilder();
243+
queryBuilder.where('inelastic_interaction_rate_avg').not().greaterThanOrNull(40000);
244+
245+
const result = await RunRepository.findAll(queryBuilder);
246+
expect(result).to.not.be.null;
247+
expect(result).to.have.lengthOf(1);
248+
result.forEach(({ inelasticInteractionRateAvg }) => {
249+
expect(inelasticInteractionRateAvg < 40000).to.be.true;
250+
});
251+
});
252+
});
253+
254+
describe('lowerThanOrNull', () => {
255+
it('should return entities with inelastic_interaction_rate_avg lower than or equal to 500000 or null', async () => {
256+
const queryBuilder = new QueryBuilder();
257+
queryBuilder.where('inelastic_interaction_rate_avg').lowerThanOrNull(500000);
258+
259+
const result = await RunRepository.findAll(queryBuilder);
260+
expect(result).to.not.be.null;
261+
expect(result).to.have.lengthOf(107);
262+
result.forEach(({ inelasticInteractionRateAvg }) => {
263+
expect(inelasticInteractionRateAvg === null || inelasticInteractionRateAvg <= 500000).to.be.true;
264+
});
265+
});
266+
267+
it('should return entities with inelastic_interaction_rate_avg lower than 500000 or null', async () => {
268+
const queryBuilder = new QueryBuilder();
269+
queryBuilder.where('inelastic_interaction_rate_avg').lowerThanOrNull(500000, true);
270+
271+
const result = await RunRepository.findAll(queryBuilder);
272+
expect(result).to.not.be.null;
273+
expect(result).to.have.lengthOf(106);
274+
result.forEach(({ inelasticInteractionRateAvg }) => {
275+
expect(inelasticInteractionRateAvg === null || inelasticInteractionRateAvg < 500000).to.be.true;
276+
});
277+
});
278+
279+
it('should return entities with not (inelastic_interaction_rate_avg <= 500000 or null)', async () => {
280+
const queryBuilder = new QueryBuilder();
281+
queryBuilder.where('inelastic_interaction_rate_avg').not().lowerThanOrNull(500000);
282+
283+
const result = await RunRepository.findAll(queryBuilder);
284+
expect(result).to.not.be.null;
285+
expect(result).to.have.lengthOf(2);
286+
result.forEach(({ inelasticInteractionRateAvg }) => {
287+
expect(inelasticInteractionRateAvg > 500000).to.be.true;
288+
});
289+
});
290+
});
211291
});
212292
};

test/lib/usecases/lhcFill/GetAllLhcFillsUseCase.test.js

Lines changed: 45 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ module.exports = () => {
111111
});
112112
});
113113

114-
it('should only contain specified stable beam durations, <= 12:00:00', async () => {
114+
it('should only contain specified stable beam durations, <= 12:00:00', async () => {
115115
getAllLhcFillsDto.query = { filter: { beamDuration: {limit: '43200', operator: '<='} } };
116116
const { lhcFills } = await new GetAllLhcFillsUseCase().execute(getAllLhcFillsDto)
117117
expect(lhcFills).to.be.an('array').and.lengthOf(4)
@@ -129,32 +129,70 @@ module.exports = () => {
129129
});
130130
});
131131

132-
it('should only contain specified stable beam durations, >= 00:01:40', async () => {
132+
it('should only contain specified stable beam durations, >= 00:01:40', async () => {
133133
getAllLhcFillsDto.query = { filter: { beamDuration: {limit: '100', operator: '>='} } };
134134
const { lhcFills } = await new GetAllLhcFillsUseCase().execute(getAllLhcFillsDto)
135135

136136
expect(lhcFills).to.be.an('array').and.lengthOf(4)
137137
lhcFills.forEach((lhcFill) => {
138138
expect(lhcFill.stableBeamsDuration).greaterThanOrEqual(100)
139139
});
140-
})
140+
})
141141

142-
it('should only contain specified stable beam durations, > 00:01:40', async () => {
142+
it('should only contain specified stable beam durations, > 00:01:40', async () => {
143143
getAllLhcFillsDto.query = { filter: { beamDuration: {limit: '100', operator: '>'} } };
144144
const { lhcFills } = await new GetAllLhcFillsUseCase().execute(getAllLhcFillsDto)
145145

146146
expect(lhcFills).to.be.an('array').and.lengthOf(1)
147147
lhcFills.forEach((lhcFill) => {
148148
expect(lhcFill.stableBeamsDuration).greaterThan(100)
149149
});
150-
})
150+
})
151151

152-
it('should only contain specified stable beam durations, = 00:00:00', async () => {
152+
it('should only contain specified stable beam durations, = 00:00:00', async () => {
153153
getAllLhcFillsDto.query = { filter: { hasStableBeams: true, beamDuration: {limit: '0', operator: '='} } };
154154
const { lhcFills } = await new GetAllLhcFillsUseCase().execute(getAllLhcFillsDto)
155155

156156
expect(lhcFills).to.be.an('array').and.lengthOf(0)
157-
})
157+
})
158+
159+
it('should only contain specified total run duration, < 00:00:00', async () => {
160+
getAllLhcFillsDto.query = { filter: { runDuration: {limit: '0', operator: '<'} } };
161+
const { lhcFills } = await new GetAllLhcFillsUseCase().execute(getAllLhcFillsDto)
162+
163+
expect(lhcFills).to.be.an('array').and.lengthOf(0)
164+
});
165+
166+
it('should only contain specified total run duration, > 00:00:00', async () => {
167+
getAllLhcFillsDto.query = { filter: { runDuration: {limit: '0', operator: '>'} } };
168+
const { lhcFills } = await new GetAllLhcFillsUseCase().execute(getAllLhcFillsDto)
169+
170+
expect(lhcFills).to.be.an('array').and.lengthOf(1)
171+
lhcFills.forEach((lhcFill) => {
172+
expect(lhcFill.statistics.runsCoverage).greaterThan(0)
173+
});
174+
});
175+
176+
it('should only contain specified total run duration, <= 00:00:00', async () => {
177+
getAllLhcFillsDto.query = { filter: { runDuration: {limit: '0', operator: '<='} } };
178+
const { lhcFills } = await new GetAllLhcFillsUseCase().execute(getAllLhcFillsDto)
179+
180+
expect(lhcFills).to.be.an('array').and.lengthOf(4)
181+
lhcFills.forEach((lhcFill) => {
182+
expect(lhcFill.statistics.runsCoverage).equals(0)
183+
});
184+
});
185+
186+
it('should only contain specified total run duration, >= 00:00:00', async () => {
187+
getAllLhcFillsDto.query = { filter: { runDuration: {limit: '0', operator: '>='} } };
188+
const { lhcFills } = await new GetAllLhcFillsUseCase().execute(getAllLhcFillsDto)
189+
190+
expect(lhcFills).to.be.an('array').and.lengthOf(5)
191+
lhcFills.forEach((lhcFill) => {
192+
expect(lhcFill.statistics.runsCoverage).greaterThanOrEqual(0)
193+
});
194+
});
195+
158196

159197
it('should only contain specified total run duration, > 04:00:00', async () => {
160198
getAllLhcFillsDto.query = { filter: { runDuration: {limit: '14400', operator: '>'} } };

0 commit comments

Comments
 (0)