Skip to content

Commit ae0423e

Browse files
authored
Merge pull request #909 from MeasureAuthoringTool/feature/MAT-8786/filter-on-cms-id
MAT-8786: allow user to search for a CMS ID containing FHIR if Qi-Cor…
2 parents 2d75acf + a44512d commit ae0423e

2 files changed

Lines changed: 89 additions & 18 deletions

File tree

src/main/java/cms/gov/madie/measure/repositories/MeasureSearchServiceImpl.java

Lines changed: 54 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import org.apache.commons.collections4.CollectionUtils;
1111
import org.apache.commons.lang3.StringUtils;
1212

13+
import org.bson.Document;
1314
import org.springframework.data.domain.Page;
1415
import org.springframework.data.domain.PageImpl;
1516
import org.springframework.data.domain.Pageable;
@@ -99,12 +100,6 @@ && isNumeric(versionParts[2])) {
99100
// provided, we need to force this criteria search
100101
break;
101102
case "cmsId":
102-
if (isNumeric(searchField)) {
103-
int number = Integer.parseInt(searchField);
104-
orConditions.add(Criteria.where("measureSet.cmsId").is(number));
105-
} else {
106-
orConditions.add(Criteria.where("measureSet.cmsId").is(searchField));
107-
}
108103
break;
109104
case "measure":
110105
orConditions.add(
@@ -136,6 +131,8 @@ public Page<MeasureListDTO> searchMeasuresByCriteria(
136131
boolean filterByCurrentUser,
137132
// TODO Remove parameter when either measureSearch or EditTestsOnVersionedMeasure is removed.
138133
String invocationSource) {
134+
List<AggregationOperation> aggregationOperations = new ArrayList<>();
135+
139136
// join measure and measure_set to lookup owner and ACL info
140137
LookupOperation lookupOperation = getLookupOperation();
141138

@@ -146,11 +143,17 @@ public Page<MeasureListDTO> searchMeasuresByCriteria(
146143
invocationSource.equals("testCase")
147144
? appConfigService.isFlagEnabled(MadieFeatureFlag.EDIT_TESTS_ON_VERSIONED_MEASURES)
148145
: appConfigService.isFlagEnabled(MadieFeatureFlag.MEASURE_SEARCH);
146+
147+
aggregationOperations.add(lookupOperation);
148+
aggregationOperations.add(unwindOperation);
149+
149150
if (measureSearchCriteria != null) {
150151
// If searchField is given and no filter is applied, then search for the searchField in
151152
// measureName and ecqmTitle
152153
if (StringUtils.isNotBlank(measureSearchCriteria.getSearchField())
153154
&& CollectionUtils.isEmpty(measureSearchCriteria.getOptionalSearchProperties())) {
155+
aggregationOperations.add(addCmsIdDisplayField());
156+
154157
String[] searchWords = measureSearchCriteria.getSearchField().split("\\s+");
155158
List<Criteria> wordCriteria = new ArrayList<>();
156159

@@ -161,12 +164,13 @@ public Page<MeasureListDTO> searchMeasuresByCriteria(
161164
new Criteria()
162165
.orOperator(
163166
Criteria.where("measureName").regex(".*" + word + ".*", "i"),
164-
Criteria.where("ecqmTitle").regex(".*" + word + ".*", "i")));
167+
Criteria.where("ecqmTitle").regex(".*" + word + ".*", "i"),
168+
Criteria.where("cmsIdDisplay").regex(".*" + word + ".*", "i")));
165169
}
166170
}
167171

168172
if (!wordCriteria.isEmpty()) {
169-
measureCriteria.andOperator(wordCriteria);
173+
aggregationOperations.add(match(new Criteria().andOperator(wordCriteria)));
170174
}
171175
}
172176
// if searchField and optional filters are provided, then search for searchField only in the
@@ -256,16 +260,20 @@ lookupOperation, unwindOperation, matchOperation, group("measureSetId")),
256260
.build();
257261
ReplaceRootOperation replaceRootOperation = replaceRoot("selectedDoc");
258262

259-
pipeline =
260-
newAggregation(
261-
lookupOperation,
262-
unwindOperation,
263-
matchMeasureSetIds,
264-
sortByVersionAndDraft,
265-
groupByMeasureSet,
266-
addHasAssociated,
267-
replaceRootOperation,
268-
facets);
263+
if (StringUtils.isNotBlank(measureSearchCriteria.getSearchField())
264+
&& measureSearchCriteria.getOptionalSearchProperties().contains("cmsId")) {
265+
aggregationOperations.add(addCmsIdDisplayField());
266+
aggregationOperations.add(matchCmsIdDisplay(measureSearchCriteria.getSearchField()));
267+
}
268+
269+
aggregationOperations.add(matchMeasureSetIds);
270+
aggregationOperations.add(sortByVersionAndDraft);
271+
aggregationOperations.add(groupByMeasureSet);
272+
aggregationOperations.add(addHasAssociated);
273+
aggregationOperations.add(replaceRootOperation);
274+
aggregationOperations.add(facets);
275+
276+
pipeline = newAggregation(aggregationOperations);
269277

270278
} else {
271279
pipeline = newAggregation(lookupOperation, unwindOperation, matchOperation, facets);
@@ -295,6 +303,34 @@ lookupOperation, unwindOperation, matchOperation, group("measureSetId")),
295303
results.get(0).getQueryResults(), pageable, results.get(0).getCount().size());
296304
}
297305

306+
// Add string field called cmsIdDisplay. If model is QI-Core, append "FHIR" to measureSet
307+
// .cmsId, else only convert measureSet.cmsId to a string
308+
private AggregationOperation addCmsIdDisplayField() {
309+
return context ->
310+
new Document(
311+
"$addFields",
312+
new Document(
313+
"cmsIdDisplay",
314+
new Document(
315+
"$cond",
316+
List.of(
317+
new Document(
318+
"$regexMatch",
319+
new Document("input", "$model").append("regex", "QI-Core")),
320+
new Document(
321+
"$concat",
322+
List.of(new Document("$toString", "$measureSet.cmsId"), "FHIR")),
323+
new Document("$toString", "$measureSet.cmsId")))));
324+
}
325+
326+
// Case-insensitive contains search
327+
private AggregationOperation matchCmsIdDisplay(String input) {
328+
return context ->
329+
new Document(
330+
"$match",
331+
new Document("cmsIdDisplay", new Document("$regex", input).append("$options", "i")));
332+
}
333+
298334
@Override
299335
public List<LibraryUsage> findLibraryUsageByLibraryName(String name) {
300336
LookupOperation lookupOperation = getLookupOperation();

src/test/java/cms/gov/madie/measure/repositories/MeasureSearchServiceImplTest.java

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,41 @@ public void testFindMyActiveMeasuresWithSearchFieldFilteredByMeasureNameAndFeatu
302302
assertEquals("test-measure-name", page1Measures.get(0).getMeasureName());
303303
}
304304

305+
@Test
306+
public void testFindMyActiveMeasuresWithSearchFieldFilteredByCmsIdAndFeatureFlagIsOn() {
307+
when(appConfigService.isFlagEnabled(MadieFeatureFlag.MEASURE_SEARCH)).thenReturn(true);
308+
309+
MeasureSetIdDTO dto1 = new MeasureSetIdDTO("set1");
310+
MeasureSetIdDTO dto2 = new MeasureSetIdDTO("set2");
311+
when(mongoTemplate.aggregate(
312+
any(Aggregation.class),
313+
ArgumentMatchers.eq(Measure.class),
314+
ArgumentMatchers.eq(MeasureSetIdDTO.class)))
315+
.thenReturn(new AggregationResults<>(List.of(dto1, dto2), new Document()));
316+
PageRequest pageRequest = PageRequest.of(0, 3);
317+
FacetDTO facetDTO =
318+
FacetDTO.builder().queryResults(List.of(measure1)).count(List.of(1)).build();
319+
320+
when(mongoTemplate.aggregate(
321+
any(), ArgumentMatchers.eq(Measure.class), ArgumentMatchers.eq(FacetDTO.class)))
322+
.thenReturn(new AggregationResults<>(List.of(facetDTO), new Document()));
323+
MeasureSearchCriteria measureSearchCriteria =
324+
MeasureSearchCriteria.builder()
325+
.searchField("28fhir")
326+
.optionalSearchProperties(List.of("cmsId"))
327+
.build();
328+
329+
Page<MeasureListDTO> page =
330+
measureAclRepository.searchMeasuresByCriteria(
331+
"john", pageRequest, measureSearchCriteria, true, "measures");
332+
333+
assertEquals(page.getTotalElements(), 1);
334+
assertEquals(page.getTotalPages(), 1);
335+
assertEquals(page.getContent().size(), 1);
336+
List<MeasureListDTO> page1Measures = page.getContent();
337+
assertEquals(measure1.getMeasureName(), page1Measures.get(0).getMeasureName());
338+
}
339+
305340
@Test
306341
public void testFindMyActiveMeasuresWithSearchFieldFilteredByModel() {
307342
when(appConfigService.isFlagEnabled(MadieFeatureFlag.MEASURE_SEARCH)).thenReturn(false);

0 commit comments

Comments
 (0)