Issue 4426 add sorting to mdm queries (#4439)

* Add sorting capability to $mdm-query-links operation

* Add exception code

* Improve code

* Add test combining pagination with sort

Co-authored-by: juan.marchionatto <juan.marchionatto@smilecdr.com>
This commit is contained in:
jmarchionatto 2023-01-20 12:25:57 -05:00 committed by GitHub
parent eec30d3387
commit 278d5cc481
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 427 additions and 93 deletions

View File

@ -0,0 +1,4 @@
---
type: add
issue: 4426
title: "Added sorting capability to $mdm-query-links operation."

View File

@ -113,6 +113,14 @@ Use the `$mdm-query-links` operation to view MDM links. The results returned are
The number of links to be returned in a page. The number of links to be returned in a page.
</td> </td>
</tr> </tr>
<tr>
<td>_sort</td>
<td>String</td>
<td>0..1</td>
<td>
The sort specification (see sort note below).
</td>
</tr>
<tr> <tr>
<td>resourceType</td> <td>resourceType</td>
<td>String</td> <td>String</td>
@ -124,6 +132,12 @@ Use the `$mdm-query-links` operation to view MDM links. The results returned are
</tbody> </tbody>
</table> </table>
Sort note: sort is specified by adding one or more comma-separated MdmLink property names prefixed by '-' (minus sign) to indicate descending order.
#### Sort specification example
```url
http://example.com/$mdm-query-links?_sort=-myScore,myCreated
```
### Example ### Example
Use an HTTP GET like `http://example.com/$mdm-query-links?matchResult=POSSIBLE_MATCH` or an HTTP POST to the following URL to invoke this operation: Use an HTTP GET like `http://example.com/$mdm-query-links?matchResult=POSSIBLE_MATCH` or an HTTP POST to the following URL to invoke this operation:

View File

@ -33,6 +33,7 @@ import ca.uhn.fhir.mdm.api.MdmQuerySearchParameters;
import ca.uhn.fhir.mdm.api.paging.MdmPageRequest; import ca.uhn.fhir.mdm.api.paging.MdmPageRequest;
import ca.uhn.fhir.mdm.dao.IMdmLinkDao; import ca.uhn.fhir.mdm.dao.IMdmLinkDao;
import ca.uhn.fhir.mdm.model.MdmPidTuple; import ca.uhn.fhir.mdm.model.MdmPidTuple;
import ca.uhn.fhir.rest.api.SortOrderEnum;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.ListUtils; import org.apache.commons.collections4.ListUtils;
@ -49,14 +50,26 @@ import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Expression; import javax.persistence.criteria.Expression;
import javax.persistence.criteria.Order;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root; import javax.persistence.criteria.Root;
import javax.validation.constraints.NotNull;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static ca.uhn.fhir.mdm.api.MdmQuerySearchParameters.GOLDEN_RESOURCE_NAME;
import static ca.uhn.fhir.mdm.api.MdmQuerySearchParameters.GOLDEN_RESOURCE_PID_NAME;
import static ca.uhn.fhir.mdm.api.MdmQuerySearchParameters.LINK_SOURCE_NAME;
import static ca.uhn.fhir.mdm.api.MdmQuerySearchParameters.MATCH_RESULT_NAME;
import static ca.uhn.fhir.mdm.api.MdmQuerySearchParameters.PARTITION_ID_NAME;
import static ca.uhn.fhir.mdm.api.MdmQuerySearchParameters.RESOURCE_TYPE_NAME;
import static ca.uhn.fhir.mdm.api.MdmQuerySearchParameters.SOURCE_PID_NAME;
public class MdmLinkDaoJpaImpl implements IMdmLinkDao<JpaPid, MdmLink> { public class MdmLinkDaoJpaImpl implements IMdmLinkDao<JpaPid, MdmLink> {
@Autowired @Autowired
IMdmLinkJpaRepository myMdmLinkDao; IMdmLinkJpaRepository myMdmLinkDao;
@ -107,7 +120,7 @@ public class MdmLinkDaoJpaImpl implements IMdmLinkDao<JpaPid, MdmLink> {
public List<JpaPid> findPidByResourceNameAndThreshold(String theResourceName, Date theHighThreshold, Pageable thePageable) { public List<JpaPid> findPidByResourceNameAndThreshold(String theResourceName, Date theHighThreshold, Pageable thePageable) {
return myMdmLinkDao.findPidByResourceNameAndThreshold(theResourceName,theHighThreshold, thePageable) return myMdmLinkDao.findPidByResourceNameAndThreshold(theResourceName,theHighThreshold, thePageable)
.stream() .stream()
.map( theResourcePids -> JpaPid.fromId(theResourcePids)) .map(JpaPid::fromId)
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
@ -115,7 +128,7 @@ public class MdmLinkDaoJpaImpl implements IMdmLinkDao<JpaPid, MdmLink> {
public List<JpaPid> findPidByResourceNameAndThresholdAndPartitionId(String theResourceName, Date theHighThreshold, List<Integer> thePartitionIds, Pageable thePageable) { public List<JpaPid> findPidByResourceNameAndThresholdAndPartitionId(String theResourceName, Date theHighThreshold, List<Integer> thePartitionIds, Pageable thePageable) {
return myMdmLinkDao.findPidByResourceNameAndThresholdAndPartitionId(theResourceName,theHighThreshold, thePartitionIds, thePageable) return myMdmLinkDao.findPidByResourceNameAndThresholdAndPartitionId(theResourceName,theHighThreshold, thePartitionIds, thePageable)
.stream() .stream()
.map( theResourcePids -> JpaPid.fromId(theResourcePids)) .map(JpaPid::fromId)
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
@ -183,8 +196,14 @@ public class MdmLinkDaoJpaImpl implements IMdmLinkDao<JpaPid, MdmLink> {
@Override @Override
@Deprecated @Deprecated
public Page<MdmLink> search(IIdType theGoldenResourceId, IIdType theSourceId, MdmMatchResultEnum theMatchResult, MdmLinkSourceEnum theLinkSource, MdmPageRequest thePageRequest, List<Integer> thePartitionId) { public Page<MdmLink> search(IIdType theGoldenResourceId, IIdType theSourceId, MdmMatchResultEnum theMatchResult,
MdmQuerySearchParameters mdmQuerySearchParameters = new MdmQuerySearchParameters(theGoldenResourceId, theSourceId, theMatchResult, theLinkSource, thePageRequest, thePartitionId, null); MdmLinkSourceEnum theLinkSource, MdmPageRequest thePageRequest, List<Integer> thePartitionIds) {
MdmQuerySearchParameters mdmQuerySearchParameters = new MdmQuerySearchParameters(thePageRequest)
.setGoldenResourceId(theGoldenResourceId)
.setSourceId(theSourceId)
.setMatchResult(theMatchResult)
.setLinkSource(theLinkSource)
.setPartitionIds(thePartitionIds);
return search(mdmQuerySearchParameters); return search(mdmQuerySearchParameters);
} }
@ -193,37 +212,14 @@ public class MdmLinkDaoJpaImpl implements IMdmLinkDao<JpaPid, MdmLink> {
CriteriaBuilder criteriaBuilder = myEntityManager.getCriteriaBuilder(); CriteriaBuilder criteriaBuilder = myEntityManager.getCriteriaBuilder();
CriteriaQuery<MdmLink> criteriaQuery = criteriaBuilder.createQuery(MdmLink.class); CriteriaQuery<MdmLink> criteriaQuery = criteriaBuilder.createQuery(MdmLink.class);
Root<MdmLink> from = criteriaQuery.from(MdmLink.class); Root<MdmLink> from = criteriaQuery.from(MdmLink.class);
List<Order> orderList = getOrderList(theParams, criteriaBuilder, from);
List<Predicate> andPredicates = new ArrayList<>(); List<Predicate> andPredicates = buildPredicates(theParams, criteriaBuilder, from);
if (theParams.getGoldenResourceId() != null) {
Predicate goldenResourcePredicate = criteriaBuilder.equal(from.get("myGoldenResourcePid").as(Long.class), (myIdHelperService.getPidOrThrowException(RequestPartitionId.allPartitions(), theParams.getGoldenResourceId())).getId());
andPredicates.add(goldenResourcePredicate);
}
if (theParams.getSourceId() != null) {
Predicate sourceIdPredicate = criteriaBuilder.equal(from.get("mySourcePid").as(Long.class), (myIdHelperService.getPidOrThrowException(RequestPartitionId.allPartitions(), theParams.getSourceId())).getId());
andPredicates.add(sourceIdPredicate);
}
if (theParams.getMatchResult() != null) {
Predicate matchResultPredicate = criteriaBuilder.equal(from.get("myMatchResult").as(MdmMatchResultEnum.class), theParams.getMatchResult());
andPredicates.add(matchResultPredicate);
}
if (theParams.getLinkSource() != null) {
Predicate linkSourcePredicate = criteriaBuilder.equal(from.get("myLinkSource").as(MdmLinkSourceEnum.class), theParams.getLinkSource());
andPredicates.add(linkSourcePredicate);
}
if (!CollectionUtils.isEmpty(theParams.getPartitionIds())) {
Expression<Integer> exp = from.get("myPartitionId").get("myPartitionId").as(Integer.class);
Predicate linkSourcePredicate = exp.in(theParams.getPartitionIds());
andPredicates.add(linkSourcePredicate);
}
if (theParams.getResourceType() != null) {
Predicate resourceTypePredicate = criteriaBuilder.equal(from.get("myGoldenResource").get("myResourceType").as(String.class), theParams.getResourceType());
andPredicates.add(resourceTypePredicate);
}
Predicate finalQuery = criteriaBuilder.and(andPredicates.toArray(new Predicate[0])); Predicate finalQuery = criteriaBuilder.and(andPredicates.toArray(new Predicate[0]));
if ( ! orderList.isEmpty()) {
criteriaQuery.orderBy(orderList);
}
TypedQuery<MdmLink> typedQuery = myEntityManager.createQuery(criteriaQuery.where(finalQuery)); TypedQuery<MdmLink> typedQuery = myEntityManager.createQuery(criteriaQuery.where(finalQuery));
CriteriaQuery<Long> countQuery = criteriaBuilder.createQuery(Long.class); CriteriaQuery<Long> countQuery = criteriaBuilder.createQuery(Long.class);
@ -232,11 +228,63 @@ public class MdmLinkDaoJpaImpl implements IMdmLinkDao<JpaPid, MdmLink> {
Long totalResults = myEntityManager.createQuery(countQuery).getSingleResult(); Long totalResults = myEntityManager.createQuery(countQuery).getSingleResult();
MdmPageRequest pageRequest = theParams.getPageRequest(); MdmPageRequest pageRequest = theParams.getPageRequest();
return new PageImpl<>(typedQuery.setFirstResult(pageRequest.getOffset()).setMaxResults(pageRequest.getCount()).getResultList(),
List<MdmLink> result = typedQuery
.setFirstResult(pageRequest.getOffset())
.setMaxResults(pageRequest.getCount())
.getResultList();
return new PageImpl<>(result,
PageRequest.of(pageRequest.getPage(), pageRequest.getCount()), PageRequest.of(pageRequest.getPage(), pageRequest.getCount()),
totalResults); totalResults);
} }
@NotNull
private List<Predicate> buildPredicates(MdmQuerySearchParameters theParams, CriteriaBuilder criteriaBuilder, Root<MdmLink> from) {
List<Predicate> andPredicates = new ArrayList<>();
if (theParams.getGoldenResourceId() != null) {
Predicate goldenResourcePredicate = criteriaBuilder.equal(from.get(GOLDEN_RESOURCE_PID_NAME).as(Long.class), (myIdHelperService.getPidOrThrowException(RequestPartitionId.allPartitions(), theParams.getGoldenResourceId())).getId());
andPredicates.add(goldenResourcePredicate);
}
if (theParams.getSourceId() != null) {
Predicate sourceIdPredicate = criteriaBuilder.equal(from.get(SOURCE_PID_NAME).as(Long.class), (myIdHelperService.getPidOrThrowException(RequestPartitionId.allPartitions(), theParams.getSourceId())).getId());
andPredicates.add(sourceIdPredicate);
}
if (theParams.getMatchResult() != null) {
Predicate matchResultPredicate = criteriaBuilder.equal(from.get(MATCH_RESULT_NAME).as(MdmMatchResultEnum.class), theParams.getMatchResult());
andPredicates.add(matchResultPredicate);
}
if (theParams.getLinkSource() != null) {
Predicate linkSourcePredicate = criteriaBuilder.equal(from.get(LINK_SOURCE_NAME).as(MdmLinkSourceEnum.class), theParams.getLinkSource());
andPredicates.add(linkSourcePredicate);
}
if (!CollectionUtils.isEmpty(theParams.getPartitionIds())) {
Expression<Integer> exp = from.get(PARTITION_ID_NAME).get(PARTITION_ID_NAME).as(Integer.class);
Predicate linkSourcePredicate = exp.in(theParams.getPartitionIds());
andPredicates.add(linkSourcePredicate);
}
if (theParams.getResourceType() != null) {
Predicate resourceTypePredicate = criteriaBuilder.equal(from.get(GOLDEN_RESOURCE_NAME).get(RESOURCE_TYPE_NAME).as(String.class), theParams.getResourceType());
andPredicates.add(resourceTypePredicate);
}
return andPredicates;
}
private List<Order> getOrderList(MdmQuerySearchParameters theParams, CriteriaBuilder criteriaBuilder, Root<MdmLink> from) {
if (CollectionUtils.isEmpty(theParams.getSort())) {
return Collections.emptyList();
}
return theParams.getSort().stream().map(sortSpec -> {
Path<Object> path = from.get(sortSpec.getParamName());
return sortSpec.getOrder() == SortOrderEnum.DESC ? criteriaBuilder.desc(path) : criteriaBuilder.asc(path);
})
.collect(Collectors.toList());
}
@Override @Override
public Optional<MdmLink> findBySourcePidAndMatchResult(JpaPid theSourcePid, MdmMatchResultEnum theMatch) { public Optional<MdmLink> findBySourcePidAndMatchResult(JpaPid theSourcePid, MdmMatchResultEnum theMatch) {
return myMdmLinkDao.findBySourcePidAndMatchResult((theSourcePid).getId(), theMatch); return myMdmLinkDao.findBySourcePidAndMatchResult((theSourcePid).getId(), theMatch);

View File

@ -103,14 +103,24 @@ public class MdmControllerSvcImpl implements IMdmControllerSvc {
@Override @Override
@Deprecated @Deprecated
public Page<MdmLinkJson> queryLinks(@Nullable String theGoldenResourceId, @Nullable String theSourceResourceId, @Nullable String theMatchResult, @Nullable String theLinkSource, MdmTransactionContext theMdmTransactionContext, MdmPageRequest thePageRequest) { public Page<MdmLinkJson> queryLinks(@Nullable String theGoldenResourceId, @Nullable String theSourceResourceId, @Nullable String theMatchResult, @Nullable String theLinkSource, MdmTransactionContext theMdmTransactionContext, MdmPageRequest thePageRequest) {
MdmQuerySearchParameters mdmQuerySearchParameters = new MdmQuerySearchParameters(theGoldenResourceId, theSourceResourceId, theMatchResult, theLinkSource, thePageRequest, null, null); MdmQuerySearchParameters mdmQuerySearchParameters = new MdmQuerySearchParameters(thePageRequest)
.setGoldenResourceId(theGoldenResourceId)
.setSourceId(theSourceResourceId)
.setMatchResult(theMatchResult)
.setLinkSource(theLinkSource);
return queryLinksFromPartitionList(mdmQuerySearchParameters, theMdmTransactionContext); return queryLinksFromPartitionList(mdmQuerySearchParameters, theMdmTransactionContext);
} }
@Override @Override
@Deprecated @Deprecated
public Page<MdmLinkJson> queryLinks(@Nullable String theGoldenResourceId, @Nullable String theSourceResourceId, @Nullable String theMatchResult, @Nullable String theLinkSource, MdmTransactionContext theMdmTransactionContext, MdmPageRequest thePageRequest, @Nullable RequestDetails theRequestDetails) { public Page<MdmLinkJson> queryLinks(@Nullable String theGoldenResourceId, @Nullable String theSourceResourceId,
MdmQuerySearchParameters mdmQuerySearchParameters = new MdmQuerySearchParameters(theGoldenResourceId, theSourceResourceId, theMatchResult, theLinkSource, thePageRequest, null, null); @Nullable String theMatchResult, @Nullable String theLinkSource, MdmTransactionContext theMdmTransactionContext,
MdmPageRequest thePageRequest, @Nullable RequestDetails theRequestDetails) {
MdmQuerySearchParameters mdmQuerySearchParameters = new MdmQuerySearchParameters(thePageRequest)
.setGoldenResourceId(theGoldenResourceId)
.setSourceId(theSourceResourceId)
.setMatchResult(theMatchResult)
.setLinkSource(theLinkSource);
return queryLinks(mdmQuerySearchParameters, theMdmTransactionContext, theRequestDetails); return queryLinks(mdmQuerySearchParameters, theMdmTransactionContext, theRequestDetails);
} }
@ -130,8 +140,16 @@ public class MdmControllerSvcImpl implements IMdmControllerSvc {
@Override @Override
@Deprecated @Deprecated
public Page<MdmLinkJson> queryLinksFromPartitionList(@Nullable String theGoldenResourceId, @Nullable String theSourceResourceId, @Nullable String theMatchResult, @Nullable String theLinkSource, MdmTransactionContext theMdmTransactionContext, MdmPageRequest thePageRequest, @Nullable List<Integer> thePartitionIds) { public Page<MdmLinkJson> queryLinksFromPartitionList(@Nullable String theGoldenResourceId, @Nullable String theSourceResourceId,
MdmQuerySearchParameters mdmQuerySearchParameters = new MdmQuerySearchParameters(theGoldenResourceId, theSourceResourceId, theMatchResult, theLinkSource, thePageRequest, thePartitionIds, null); @Nullable String theMatchResult, @Nullable String theLinkSource,
MdmTransactionContext theMdmTransactionContext, MdmPageRequest thePageRequest,
@Nullable List<Integer> thePartitionIds) {
MdmQuerySearchParameters mdmQuerySearchParameters = new MdmQuerySearchParameters(thePageRequest)
.setGoldenResourceId(theGoldenResourceId)
.setSourceId(theSourceResourceId)
.setMatchResult(theMatchResult)
.setLinkSource(theLinkSource)
.setPartitionIds(thePartitionIds);
return queryLinksFromPartitionList(mdmQuerySearchParameters, theMdmTransactionContext); return queryLinksFromPartitionList(mdmQuerySearchParameters, theMdmTransactionContext);
} }
@ -214,7 +232,6 @@ public class MdmControllerSvcImpl implements IMdmControllerSvc {
if (theBatchSize != null && theBatchSize.getValue() !=null && theBatchSize.getValue().longValue() > 0) { if (theBatchSize != null && theBatchSize.getValue() !=null && theBatchSize.getValue().longValue() > 0) {
params.setBatchSize(theBatchSize.getValue().intValue()); params.setBatchSize(theBatchSize.getValue().intValue());
} }
ReadPartitionIdRequestDetails details= new ReadPartitionIdRequestDetails(null, RestOperationTypeEnum.EXTENDED_OPERATION_SERVER, null, null, null);
params.setRequestPartitionId(RequestPartitionId.allPartitions()); params.setRequestPartitionId(RequestPartitionId.allPartitions());
theUrls.forEach(params::addUrl); theUrls.forEach(params::addUrl);

View File

@ -52,21 +52,33 @@ public class MdmLinkQuerySvcImplSvc implements IMdmLinkQuerySvc {
@Deprecated @Deprecated
@Transactional @Transactional
public Page<MdmLinkJson> queryLinks(IIdType theGoldenResourceId, IIdType theSourceResourceId, MdmMatchResultEnum theMatchResult, MdmLinkSourceEnum theLinkSource, MdmTransactionContext theMdmContext, MdmPageRequest thePageRequest) { public Page<MdmLinkJson> queryLinks(IIdType theGoldenResourceId, IIdType theSourceResourceId, MdmMatchResultEnum theMatchResult, MdmLinkSourceEnum theLinkSource, MdmTransactionContext theMdmContext, MdmPageRequest thePageRequest) {
MdmQuerySearchParameters mdmQuerySearchParameters = new MdmQuerySearchParameters(theGoldenResourceId, theSourceResourceId, theMatchResult, theLinkSource, thePageRequest, null, null); MdmQuerySearchParameters mdmQuerySearchParameters = new MdmQuerySearchParameters(thePageRequest)
.setGoldenResourceId(theGoldenResourceId)
.setSourceId(theSourceResourceId)
.setMatchResult(theMatchResult)
.setLinkSource(theLinkSource);
return queryLinks(mdmQuerySearchParameters, theMdmContext); return queryLinks(mdmQuerySearchParameters, theMdmContext);
} }
@Override @Override
@Deprecated @Deprecated
@Transactional @Transactional
public Page<MdmLinkJson> queryLinks(IIdType theGoldenResourceId, IIdType theSourceResourceId, MdmMatchResultEnum theMatchResult, MdmLinkSourceEnum theLinkSource, MdmTransactionContext theMdmContext, MdmPageRequest thePageRequest, List<Integer> thePartitionId) { public Page<MdmLinkJson> queryLinks(IIdType theGoldenResourceId, IIdType theSourceResourceId, MdmMatchResultEnum theMatchResult, MdmLinkSourceEnum theLinkSource, MdmTransactionContext theMdmContext, MdmPageRequest thePageRequest, List<Integer> thePartitionIds) {
MdmQuerySearchParameters mdmQuerySearchParameters = new MdmQuerySearchParameters(theGoldenResourceId, theSourceResourceId, theMatchResult, theLinkSource, thePageRequest, thePartitionId, null); MdmQuerySearchParameters mdmQuerySearchParameters = new MdmQuerySearchParameters(thePageRequest)
.setGoldenResourceId(theGoldenResourceId)
.setSourceId(theSourceResourceId)
.setMatchResult(theMatchResult)
.setLinkSource(theLinkSource)
.setPartitionIds(thePartitionIds);
return queryLinks(mdmQuerySearchParameters, theMdmContext); return queryLinks(mdmQuerySearchParameters, theMdmContext);
} }
@Override @Override
@Transactional @Transactional
public Page<MdmLinkJson> queryLinks(MdmQuerySearchParameters theMdmQuerySearchParameters, MdmTransactionContext theMdmContext) { public Page<MdmLinkJson> queryLinks(MdmQuerySearchParameters theMdmQuerySearchParameters, MdmTransactionContext theMdmContext) {
@SuppressWarnings("unchecked")
Page<? extends IMdmLink> mdmLinks = myMdmLinkDaoSvc.executeTypedQuery(theMdmQuerySearchParameters); Page<? extends IMdmLink> mdmLinks = myMdmLinkDaoSvc.executeTypedQuery(theMdmQuerySearchParameters);
return mdmLinks.map(myMdmModelConverterSvc::toJson); return mdmLinks.map(myMdmModelConverterSvc::toJson);
} }
@ -80,9 +92,14 @@ public class MdmLinkQuerySvcImplSvc implements IMdmLinkQuerySvc {
@Override @Override
@Transactional @Transactional
public Page<MdmLinkJson> getDuplicateGoldenResources(MdmTransactionContext theMdmContext, MdmPageRequest thePageRequest, List<Integer> thePartitionId, String theRequestResourceType) { public Page<MdmLinkJson> getDuplicateGoldenResources(MdmTransactionContext theMdmContext, MdmPageRequest thePageRequest,
MdmQuerySearchParameters mdmQuerySearchParameters = List<Integer> thePartitionIds, String theRequestResourceType) {
new MdmQuerySearchParameters(null, null, MdmMatchResultEnum.POSSIBLE_DUPLICATE, null, thePageRequest, thePartitionId, theRequestResourceType); MdmQuerySearchParameters mdmQuerySearchParameters = new MdmQuerySearchParameters(thePageRequest)
.setMatchResult(MdmMatchResultEnum.POSSIBLE_DUPLICATE)
.setPartitionIds(thePartitionIds)
.setResourceType(theRequestResourceType);
@SuppressWarnings("unchecked")
Page<? extends IMdmLink> mdmLinkPage = myMdmLinkDaoSvc.executeTypedQuery(mdmQuerySearchParameters); Page<? extends IMdmLink> mdmLinkPage = myMdmLinkDaoSvc.executeTypedQuery(mdmQuerySearchParameters);
return mdmLinkPage.map(myMdmModelConverterSvc::toJson); return mdmLinkPage.map(myMdmModelConverterSvc::toJson);
} }

View File

@ -10,23 +10,30 @@ import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import ca.uhn.fhir.util.ParametersUtil;
import ca.uhn.fhir.util.StopWatch; import ca.uhn.fhir.util.StopWatch;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.hl7.fhir.dstu3.model.UnsignedIntType; import org.hl7.fhir.dstu3.model.UnsignedIntType;
import org.hl7.fhir.instance.model.api.IAnyResource; import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.instance.model.api.IBase;
import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.r4.model.BooleanType; import org.hl7.fhir.r4.model.BooleanType;
import org.hl7.fhir.r4.model.DecimalType;
import org.hl7.fhir.r4.model.IdType; import org.hl7.fhir.r4.model.IdType;
import org.hl7.fhir.r4.model.Parameters; import org.hl7.fhir.r4.model.Parameters;
import org.hl7.fhir.r4.model.Patient; import org.hl7.fhir.r4.model.Patient;
import org.hl7.fhir.r4.model.StringType; import org.hl7.fhir.r4.model.StringType;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import java.util.Comparator;
import java.util.List; import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.MatcherAssert.assertThat;
@ -35,13 +42,16 @@ import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.lessThanOrEqualTo; import static org.hamcrest.Matchers.lessThanOrEqualTo;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail; import static org.junit.jupiter.api.Assertions.fail;
public class MdmProviderQueryLinkR4Test extends BaseLinkR4Test { public class MdmProviderQueryLinkR4Test extends BaseLinkR4Test {
private static final Logger ourLog = LoggerFactory.getLogger(MdmProviderQueryLinkR4Test.class); private static final Logger ourLog = LoggerFactory.getLogger(MdmProviderQueryLinkR4Test.class);
private static final int MDM_LINK_PROPERTY_COUNT = 9;
private static final StringType RESOURCE_TYPE_PATIENT = new StringType("Patient"); private static final StringType RESOURCE_TYPE_PATIENT = new StringType("Patient");
private static final StringType RESOURCE_TYPE_OBSERVATION = new StringType("Observation"); private static final StringType RESOURCE_TYPE_OBSERVATION = new StringType("Observation");
private StringType myLinkSource; private StringType myLinkSource;
private StringType myGoldenResource1Id; private StringType myGoldenResource1Id;
private StringType myGoldenResource2Id; private StringType myGoldenResource2Id;
@ -74,27 +84,180 @@ public class MdmProviderQueryLinkR4Test extends BaseLinkR4Test {
@Test @Test
public void testQueryLinkOneMatch() { public void testQueryLinkOneMatch() {
Parameters result = (Parameters) myMdmProvider.queryLinks(mySourcePatientId, myPatientId, null, null, new UnsignedIntType(0), new UnsignedIntType(10), myRequestDetails, null); Parameters result = (Parameters) myMdmProvider.queryLinks(mySourcePatientId, myPatientId, null, null, new UnsignedIntType(0), new UnsignedIntType(10), new StringType(), myRequestDetails, null);
ourLog.debug(myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(result)); ourLog.debug(myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(result));
List<Parameters.ParametersParameterComponent> list = getParametersByName(result, "link"); List<Parameters.ParametersParameterComponent> list = getParametersByName(result, "link");
assertThat(list, hasSize(1)); assertThat(list, hasSize(1));
List<Parameters.ParametersParameterComponent> part = list.get(0).getPart(); List<Parameters.ParametersParameterComponent> part = list.get(0).getPart();
assertMdmLink(7, part, mySourcePatientId.getValue(), myPatientId.getValue(), MdmMatchResultEnum.POSSIBLE_MATCH, "false", "true", null); assertMdmLink(MDM_LINK_PROPERTY_COUNT, part, mySourcePatientId.getValue(), myPatientId.getValue(), MdmMatchResultEnum.POSSIBLE_MATCH, "false", "true", null);
} }
@Test @Test
public void testQueryLinkWithResourceType() { public void testQueryLinkWithResourceType() {
Parameters result = (Parameters) myMdmProvider.queryLinks(null, null, null, null, new UnsignedIntType(0), new UnsignedIntType(10), myRequestDetails, RESOURCE_TYPE_PATIENT); Parameters result = (Parameters) myMdmProvider.queryLinks(null, null, null, null, new UnsignedIntType(0), new UnsignedIntType(10), new StringType(), myRequestDetails, RESOURCE_TYPE_PATIENT);
ourLog.debug(myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(result)); ourLog.debug(myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(result));
List<Parameters.ParametersParameterComponent> list = getParametersByName(result, "link"); List<Parameters.ParametersParameterComponent> list = getParametersByName(result, "link");
assertThat("All resources with Patient type found", list, hasSize(3)); assertThat("All resources with Patient type found", list, hasSize(3));
List<Parameters.ParametersParameterComponent> part = list.get(0).getPart(); List<Parameters.ParametersParameterComponent> part = list.get(0).getPart();
assertMdmLink(7, part, mySourcePatientId.getValue(), myPatientId.getValue(), MdmMatchResultEnum.POSSIBLE_MATCH, "false", "true", null); assertMdmLink(MDM_LINK_PROPERTY_COUNT, part, mySourcePatientId.getValue(), myPatientId.getValue(), MdmMatchResultEnum.POSSIBLE_MATCH, "false", "true", null);
} }
@Nested
public class QueryLinkWithSort {
@Test
public void byCreatedDescending() {
Parameters result = (Parameters) myMdmProvider.queryLinks(null, null, null, null,
new UnsignedIntType(0), new UnsignedIntType(10), new StringType("-myCreated"),
myRequestDetails, new StringType("Patient"));
List<Parameters.ParametersParameterComponent> linkList = getParametersByName(result, "link");
assertThat(linkList, hasSize(3));
List<Long> createdDates = linkList.stream().map(this::extractCreated).collect(Collectors.toList());
// by created descending
Comparator<Long> comp = Comparator.comparingLong( (Long e) -> e).reversed();
List<Long> expected = createdDates.stream().sorted(comp).collect(Collectors.toList());
assertEquals(expected, createdDates);
}
@Test
public void byScoreDescending() {
addScoresToLinksInCreationOrder(List.of(.5d, .1d, .3d));
Parameters result = (Parameters) myMdmProvider.queryLinks(null, null, null, null,
new UnsignedIntType(0), new UnsignedIntType(10), new StringType("-myScore"),
myRequestDetails, new StringType("Patient"));
List<Parameters.ParametersParameterComponent> linkList = getParametersByName(result, "link");
assertThat(linkList, hasSize(3));
List<Double> scores = linkList.stream().map(this::extractScore).collect(Collectors.toList());
// by score descending
Comparator<Double> comp = Comparator.comparingDouble( (Double e) -> e).reversed();
List<Double> expected = scores.stream().sorted(comp).collect(Collectors.toList());
assertEquals(expected, scores);
}
@Test
public void byScoreDescendingAndThenCreated() {
addScoresToLinksInCreationOrder(List.of(.4d, .4d, .3d));
Parameters result = (Parameters) myMdmProvider.queryLinks(null, null, null, null,
new UnsignedIntType(0), new UnsignedIntType(10), new StringType("-myScore,-myCreated"),
myRequestDetails, new StringType("Patient"));
List<Parameters.ParametersParameterComponent> linkList = getParametersByName(result, "link");
assertThat(linkList, hasSize(3));
List<Pair<Long, Double>> resultUpdatedScorePairs = linkList.stream()
.map(l -> Pair.of(extractCreated(l), extractScore(l))).collect(Collectors.toList());
// by desc score then desc created
Comparator<Pair<Long, Double>> comp = Comparator
.comparing( (Pair<Long, Double> p) -> p.getRight(), Comparator.reverseOrder() )
.thenComparing(Pair::getLeft, Comparator.reverseOrder() );
List<Pair<Long, Double>> expected = resultUpdatedScorePairs.stream().sorted(comp).collect(Collectors.toList());
assertEquals(expected, resultUpdatedScorePairs);
}
@Test
public void testPaginationWhenSorting() {
addTwoMoreMdmLinks();
addScoresToLinksInCreationOrder(List.of(.5d, .1d, .3d, .8d, .6d));
List<Double> expectedScoresPage1 = List.of(.8d, .6d, .5d);
List<Double> expectedScoresPage2 = List.of(.3d, .1d);
int pageSize = 3;
// first page
Parameters page1 = (Parameters) myMdmProvider.queryLinks(null, null, null, null,
new UnsignedIntType(0), new UnsignedIntType(pageSize), new StringType("-myScore"),
myRequestDetails, new StringType("Patient"));
List<Parameters.ParametersParameterComponent> linkListPage1 = getParametersByName(page1, "link");
assertThat(linkListPage1, hasSize(pageSize));
List<Double> scoresPage1 = linkListPage1.stream().map(this::extractScore).collect(Collectors.toList());
assertEquals(expectedScoresPage1, scoresPage1);
// second page
Parameters page2 = (Parameters) myMdmProvider.queryLinks(null, null, null, null,
new UnsignedIntType(pageSize), new UnsignedIntType(pageSize), new StringType("-myScore"),
myRequestDetails, new StringType("Patient"));
List<Parameters.ParametersParameterComponent> linkListPage2 = getParametersByName(page2, "link");
assertThat(linkListPage2, hasSize(2));
List<Double> scoresPage2 = linkListPage2.stream().map(this::extractScore).collect(Collectors.toList());
assertEquals(expectedScoresPage2, scoresPage2);
}
private Long extractCreated(Parameters.ParametersParameterComponent theParamComponent) {
Optional<IBase> opt = ParametersUtil.getParameterPartValue(myFhirContext, theParamComponent, "linkUpdated");
assertTrue(opt.isPresent());
DecimalType createdDateDt = (DecimalType) opt.get();
return createdDateDt.getValue().longValue();
}
private Double extractScore(Parameters.ParametersParameterComponent theParamComponent) {
Optional<IBase> opt = ParametersUtil.getParameterPartValue(myFhirContext, theParamComponent, "score");
assertTrue(opt.isPresent());
DecimalType scoreIntegerDt = (DecimalType) opt.get();
assertNotNull(scoreIntegerDt.getValue());
return scoreIntegerDt.getValue().doubleValue();
}
}
private void addTwoMoreMdmLinks() {
createPatientAndUpdateLinks(buildFrankPatient());
// Add a possible duplicate
myLinkSource = new StringType(MdmLinkSourceEnum.AUTO.name());
Patient sourcePatient1 = createGoldenPatient();
myGoldenResource1Id = new StringType(sourcePatient1.getIdElement().toVersionless().getValue());
JpaPid sourcePatient1Pid = runInTransaction(()->myIdHelperService.getPidOrNull(RequestPartitionId.allPartitions(), sourcePatient1));
Patient sourcePatient2 = createGoldenPatient();
myGoldenResource2Id = new StringType(sourcePatient2.getIdElement().toVersionless().getValue());
JpaPid sourcePatient2Pid = runInTransaction(()->myIdHelperService.getPidOrNull(RequestPartitionId.allPartitions(), sourcePatient2));
MdmLink possibleDuplicateMdmLink = (MdmLink) myMdmLinkDaoSvc.newMdmLink();
possibleDuplicateMdmLink.setGoldenResourcePersistenceId(sourcePatient1Pid);
possibleDuplicateMdmLink.setSourcePersistenceId(sourcePatient2Pid);
possibleDuplicateMdmLink.setMatchResult(MdmMatchResultEnum.POSSIBLE_DUPLICATE).setLinkSource(MdmLinkSourceEnum.AUTO);
saveLink(possibleDuplicateMdmLink);
}
private void addScoresToLinksInCreationOrder(List<Double> theScores) {
List<MdmLink> links = myMdmLinkDao.findAll();
assertThat(links, hasSize(theScores.size()));
links.sort( Comparator.comparing(MdmLink::getCreated) );
for (int i = 0; i < links.size(); i++) {
MdmLink link = links.get(i);
link.setScore( theScores.get(i) );
myMdmLinkDao.save(link);
}
}
@Test @Test
public void testQueryLinkWithResourceTypeNoMatch() { public void testQueryLinkWithResourceTypeNoMatch() {
Parameters result = (Parameters) myMdmProvider.queryLinks(null, null, null, null, new UnsignedIntType(0), new UnsignedIntType(10), myRequestDetails, RESOURCE_TYPE_OBSERVATION); Parameters result = (Parameters) myMdmProvider.queryLinks(null, null, null, null, new UnsignedIntType(0), new UnsignedIntType(10), new StringType(), myRequestDetails, RESOURCE_TYPE_OBSERVATION);
ourLog.debug(myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(result)); ourLog.debug(myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(result));
List<Parameters.ParametersParameterComponent> list = getParametersByName(result, "link"); List<Parameters.ParametersParameterComponent> list = getParametersByName(result, "link");
assertThat(list, hasSize(0)); assertThat(list, hasSize(0));
@ -110,7 +273,7 @@ public class MdmProviderQueryLinkR4Test extends BaseLinkR4Test {
int count = 2; int count = 2;
StopWatch sw = new StopWatch(); StopWatch sw = new StopWatch();
while (true) { while (true) {
Parameters result = (Parameters) myMdmProvider.queryLinks(null, null, null, myLinkSource, new UnsignedIntType(offset), new UnsignedIntType(count), myRequestDetails, null); Parameters result = (Parameters) myMdmProvider.queryLinks(null, null, null, myLinkSource, new UnsignedIntType(offset), new UnsignedIntType(count), new StringType(), myRequestDetails, null);
List<Parameters.ParametersParameterComponent> parameter = result.getParameter(); List<Parameters.ParametersParameterComponent> parameter = result.getParameter();
@ -156,7 +319,8 @@ public class MdmProviderQueryLinkR4Test extends BaseLinkR4Test {
null, myLinkSource, null, myLinkSource,
new UnsignedIntType(offset), new UnsignedIntType(offset),
new UnsignedIntType(count), new UnsignedIntType(count),
myRequestDetails, new StringType(),
myRequestDetails,
null); null);
} catch (InvalidRequestException e) { } catch (InvalidRequestException e) {
//Then //Then
@ -173,6 +337,7 @@ public class MdmProviderQueryLinkR4Test extends BaseLinkR4Test {
null, myLinkSource, null, myLinkSource,
new UnsignedIntType(offset), new UnsignedIntType(offset),
new UnsignedIntType(count), new UnsignedIntType(count),
new StringType(),
myRequestDetails, myRequestDetails,
null); null);
} catch (InvalidRequestException e) { } catch (InvalidRequestException e) {
@ -190,6 +355,7 @@ public class MdmProviderQueryLinkR4Test extends BaseLinkR4Test {
null, myLinkSource, null, myLinkSource,
new UnsignedIntType(offset), new UnsignedIntType(offset),
new UnsignedIntType(count), new UnsignedIntType(count),
new StringType(),
myRequestDetails, myRequestDetails,
null); null);
} catch (InvalidRequestException e) { } catch (InvalidRequestException e) {
@ -206,12 +372,12 @@ public class MdmProviderQueryLinkR4Test extends BaseLinkR4Test {
IAnyResource goldenResource = getGoldenResourceFromTargetResource(patient); IAnyResource goldenResource = getGoldenResourceFromTargetResource(patient);
IIdType goldenResourceId = goldenResource.getIdElement().toVersionless(); IIdType goldenResourceId = goldenResource.getIdElement().toVersionless();
Parameters result = (Parameters) myMdmProvider.queryLinks(null, null, null, myLinkSource, new UnsignedIntType(0), new UnsignedIntType(10), myRequestDetails, null); Parameters result = (Parameters) myMdmProvider.queryLinks(null, null, null, myLinkSource, new UnsignedIntType(0), new UnsignedIntType(10), new StringType(), myRequestDetails, null);
ourLog.debug(myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(result)); ourLog.debug(myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(result));
List<Parameters.ParametersParameterComponent> list = getParametersByName(result, "link"); List<Parameters.ParametersParameterComponent> list = getParametersByName(result, "link");
assertThat(list, hasSize(4)); assertThat(list, hasSize(4));
List<Parameters.ParametersParameterComponent> part = list.get(3).getPart(); List<Parameters.ParametersParameterComponent> part = list.get(3).getPart();
assertMdmLink(7, part, goldenResourceId.getValue(), patientId.getValue(), MdmMatchResultEnum.MATCH, "false", "false", "2"); assertMdmLink(MDM_LINK_PROPERTY_COUNT, part, goldenResourceId.getValue(), patientId.getValue(), MdmMatchResultEnum.MATCH, "false", "false", "2");
} }
@Test @Test

View File

@ -106,8 +106,9 @@ public class MdmControllerSvcImplTest extends BaseLinkR4Test {
assertEquals(MdmLinkSourceEnum.AUTO, link.getLinkSource()); assertEquals(MdmLinkSourceEnum.AUTO, link.getLinkSource());
assertLinkCount(2); assertLinkCount(2);
MdmQuerySearchParameters params = new MdmQuerySearchParameters(null, myPatientId.getIdElement().getValue(), null, null, MdmPageRequest pageRequest = new MdmPageRequest((Integer) null, null, DEFAULT_PAGE_SIZE, MAX_PAGE_SIZE);
new MdmPageRequest((Integer) null, null, DEFAULT_PAGE_SIZE, MAX_PAGE_SIZE), null, null); MdmQuerySearchParameters params = new MdmQuerySearchParameters(pageRequest)
.setSourceId(myPatientId.getIdElement().getValue());
Page<MdmLinkJson> resultPage = myMdmControllerSvc.queryLinks(params, Page<MdmLinkJson> resultPage = myMdmControllerSvc.queryLinks(params,
new MdmTransactionContext(MdmTransactionContext.OperationType.QUERY_LINKS), new MdmTransactionContext(MdmTransactionContext.OperationType.QUERY_LINKS),

View File

@ -20,16 +20,41 @@ package ca.uhn.fhir.mdm.api;
* #L% * #L%
*/ */
import ca.uhn.fhir.i18n.Msg;
import ca.uhn.fhir.mdm.api.paging.MdmPageRequest; import ca.uhn.fhir.mdm.api.paging.MdmPageRequest;
import ca.uhn.fhir.mdm.provider.MdmControllerUtil; import ca.uhn.fhir.mdm.provider.MdmControllerUtil;
import ca.uhn.fhir.rest.api.SortOrderEnum;
import ca.uhn.fhir.rest.api.SortSpec;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.provider.ProviderConstants; import ca.uhn.fhir.rest.server.provider.ProviderConstants;
import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringBuilder;
import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IIdType;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Set;
import static org.hibernate.internal.util.StringHelper.isBlank;
public class MdmQuerySearchParameters { public class MdmQuerySearchParameters {
public static final String GOLDEN_RESOURCE_PID_NAME = "myGoldenResourcePid";
public static final String SOURCE_PID_NAME = "mySourcePid";
public static final String MATCH_RESULT_NAME = "myMatchResult";
public static final String LINK_SOURCE_NAME = "myLinkSource";
public static final String PARTITION_ID_NAME = "myPartitionId";
public static final String GOLDEN_RESOURCE_NAME = "myGoldenResource";
public static final String RESOURCE_TYPE_NAME = "myResourceType";
public static final String CREATED_NAME = "myCreated";
public static final String UPDATED_NAME = "myUpdated";
public static final String SCORE_NAME = "myScore";
public static final Set<String> ourValidSortParameters = Set.of(
GOLDEN_RESOURCE_PID_NAME, SOURCE_PID_NAME, MATCH_RESULT_NAME, LINK_SOURCE_NAME, PARTITION_ID_NAME, GOLDEN_RESOURCE_NAME,
RESOURCE_TYPE_NAME, CREATED_NAME, UPDATED_NAME, SCORE_NAME
);
private IIdType myGoldenResourceId; private IIdType myGoldenResourceId;
private IIdType mySourceId; private IIdType mySourceId;
private MdmMatchResultEnum myMatchResult; private MdmMatchResultEnum myMatchResult;
@ -37,9 +62,16 @@ public class MdmQuerySearchParameters {
private MdmPageRequest myPageRequest; private MdmPageRequest myPageRequest;
private List<Integer> myPartitionIds; private List<Integer> myPartitionIds;
private String myResourceType; private String myResourceType;
private final List<SortSpec> mySort = new ArrayList<>();
@Deprecated(since = "2023.02.R01", forRemoval = true)
public MdmQuerySearchParameters() {} public MdmQuerySearchParameters() {}
public MdmQuerySearchParameters(MdmPageRequest thePageRequest) {
setPageRequest(thePageRequest);
}
@Deprecated(since = "2023.02.R01", forRemoval = true)
public MdmQuerySearchParameters(@Nullable String theGoldenResourceId, @Nullable String theSourceId, @Nullable String theMatchResult, @Nullable String theLinkSource, MdmPageRequest thePageRequest, @Nullable List<Integer> thePartitionIds, @Nullable String theResourceType) { public MdmQuerySearchParameters(@Nullable String theGoldenResourceId, @Nullable String theSourceId, @Nullable String theMatchResult, @Nullable String theLinkSource, MdmPageRequest thePageRequest, @Nullable List<Integer> thePartitionIds, @Nullable String theResourceType) {
setGoldenResourceId(theGoldenResourceId); setGoldenResourceId(theGoldenResourceId);
setSourceId(theSourceId); setSourceId(theSourceId);
@ -50,6 +82,7 @@ public class MdmQuerySearchParameters {
setResourceType(theResourceType); setResourceType(theResourceType);
} }
@Deprecated(since = "2023.02.R01", forRemoval = true)
public MdmQuerySearchParameters(@Nullable IIdType theGoldenResourceId, @Nullable IIdType theSourceId, @Nullable MdmMatchResultEnum theMatchResult, @Nullable MdmLinkSourceEnum theLinkSource, MdmPageRequest thePageRequest, @Nullable List<Integer> thePartitionIds, @Nullable String theResourceType) { public MdmQuerySearchParameters(@Nullable IIdType theGoldenResourceId, @Nullable IIdType theSourceId, @Nullable MdmMatchResultEnum theMatchResult, @Nullable MdmLinkSourceEnum theLinkSource, MdmPageRequest thePageRequest, @Nullable List<Integer> thePartitionIds, @Nullable String theResourceType) {
setGoldenResourceId(theGoldenResourceId); setGoldenResourceId(theGoldenResourceId);
setSourceId(theSourceId); setSourceId(theSourceId);
@ -64,52 +97,56 @@ public class MdmQuerySearchParameters {
return myGoldenResourceId; return myGoldenResourceId;
} }
public void setGoldenResourceId(IIdType theGoldenResourceId) { public MdmQuerySearchParameters setGoldenResourceId(IIdType theGoldenResourceId) {
myGoldenResourceId = theGoldenResourceId; myGoldenResourceId = theGoldenResourceId;
return this;
} }
public void setGoldenResourceId(String theGoldenResourceId) { public MdmQuerySearchParameters setGoldenResourceId(String theGoldenResourceId) {
IIdType goldenResourceId = MdmControllerUtil.extractGoldenResourceIdDtOrNull(ProviderConstants.MDM_QUERY_LINKS_GOLDEN_RESOURCE_ID, theGoldenResourceId); myGoldenResourceId = MdmControllerUtil.extractGoldenResourceIdDtOrNull(ProviderConstants.MDM_QUERY_LINKS_GOLDEN_RESOURCE_ID, theGoldenResourceId);
myGoldenResourceId = goldenResourceId; return this;
} }
public IIdType getSourceId() { public IIdType getSourceId() {
return mySourceId; return mySourceId;
} }
public void setSourceId(IIdType theSourceId) { public MdmQuerySearchParameters setSourceId(IIdType theSourceId) {
mySourceId = theSourceId; mySourceId = theSourceId;
return this;
} }
public void setSourceId(String theSourceId) { public MdmQuerySearchParameters setSourceId(String theSourceId) {
IIdType sourceId = MdmControllerUtil.extractSourceIdDtOrNull(ProviderConstants.MDM_QUERY_LINKS_RESOURCE_ID, theSourceId); mySourceId = MdmControllerUtil.extractSourceIdDtOrNull(ProviderConstants.MDM_QUERY_LINKS_RESOURCE_ID, theSourceId);
mySourceId = sourceId; return this;
} }
public MdmMatchResultEnum getMatchResult() { public MdmMatchResultEnum getMatchResult() {
return myMatchResult; return myMatchResult;
} }
public void setMatchResult(MdmMatchResultEnum theMatchResult) { public MdmQuerySearchParameters setMatchResult(MdmMatchResultEnum theMatchResult) {
myMatchResult = theMatchResult; myMatchResult = theMatchResult;
return this;
} }
public void setMatchResult(String theMatchResult) { public MdmQuerySearchParameters setMatchResult(String theMatchResult) {
MdmMatchResultEnum matchResult = MdmControllerUtil.extractMatchResultOrNull(theMatchResult); myMatchResult = MdmControllerUtil.extractMatchResultOrNull(theMatchResult);
myMatchResult = matchResult; return this;
} }
public MdmLinkSourceEnum getLinkSource() { public MdmLinkSourceEnum getLinkSource() {
return myLinkSource; return myLinkSource;
} }
public void setLinkSource(MdmLinkSourceEnum theLinkSource) { public MdmQuerySearchParameters setLinkSource(MdmLinkSourceEnum theLinkSource) {
myLinkSource = theLinkSource; myLinkSource = theLinkSource;
return this;
} }
public void setLinkSource(String theLinkSource) { public MdmQuerySearchParameters setLinkSource(String theLinkSource) {
MdmLinkSourceEnum linkSource = MdmControllerUtil.extractLinkSourceOrNull(theLinkSource); myLinkSource = MdmControllerUtil.extractLinkSourceOrNull(theLinkSource);
myLinkSource = linkSource; return this;
} }
public MdmPageRequest getPageRequest() { public MdmPageRequest getPageRequest() {
@ -124,28 +161,51 @@ public class MdmQuerySearchParameters {
return myPartitionIds; return myPartitionIds;
} }
public void setPartitionIds(List<Integer> thePartitionIds) { public MdmQuerySearchParameters setPartitionIds(List<Integer> thePartitionIds) {
myPartitionIds = thePartitionIds; myPartitionIds = thePartitionIds;
return this;
} }
public String getResourceType() { public String getResourceType() {
return myResourceType; return myResourceType;
} }
public void setResourceType(String theResourceType) { public MdmQuerySearchParameters setResourceType(String theResourceType) {
myResourceType = theResourceType; myResourceType = theResourceType;
return this;
}
public List<SortSpec> getSort() {
return mySort;
}
public MdmQuerySearchParameters setSort(String theSortString) {
if (isBlank(theSortString)) {
return this;
}
for (String param : theSortString.split(",")) {
String p = (param.startsWith("-") ? param.substring(1) : param).trim();
if ( ! MdmQuerySearchParameters.ourValidSortParameters.contains(p)) {
throw new InvalidRequestException(Msg.code(2233) + "Unrecognized sort parameter: " + p + ". Valid parameters are: " +
String.join(", ", MdmQuerySearchParameters.ourValidSortParameters));
}
SortOrderEnum order = param.startsWith("-") ? SortOrderEnum.DESC : SortOrderEnum.ASC;
mySort.add( new SortSpec(p, order) );
}
return this;
} }
@Override @Override
public String toString() { public String toString() {
return new ToStringBuilder(this) return new ToStringBuilder(this)
.append("myGoldenResourceId", myGoldenResourceId) .append(GOLDEN_RESOURCE_PID_NAME, myGoldenResourceId)
.append("mySourceId", mySourceId) .append(SOURCE_PID_NAME, mySourceId)
.append("myMatchResult", myMatchResult) .append(MATCH_RESULT_NAME, myMatchResult)
.append("myLinkSource", myLinkSource) .append(LINK_SOURCE_NAME, myLinkSource)
.append("myPartitionId", myPartitionIds) .append(PARTITION_ID_NAME, myPartitionIds)
.append(RESOURCE_TYPE_NAME, myResourceType)
.append("myPageRequest", myPageRequest) .append("myPageRequest", myPageRequest)
.append("myResourceType", myResourceType)
.toString(); .toString();
} }

View File

@ -20,8 +20,8 @@ package ca.uhn.fhir.mdm.provider;
* #L% * #L%
*/ */
import ca.uhn.fhir.i18n.Msg;
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.i18n.Msg;
import ca.uhn.fhir.mdm.api.MdmLinkJson; import ca.uhn.fhir.mdm.api.MdmLinkJson;
import ca.uhn.fhir.mdm.api.MdmMatchResultEnum; import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
import ca.uhn.fhir.mdm.api.paging.MdmPageLinkBuilder; import ca.uhn.fhir.mdm.api.paging.MdmPageLinkBuilder;
@ -115,7 +115,8 @@ public abstract class BaseMdmProvider {
return theString.getValue(); return theString.getValue();
} }
protected IBaseParameters parametersFromMdmLinks(Page<MdmLinkJson> theMdmLinkStream, boolean includeResultAndSource, ServletRequestDetails theServletRequestDetails, MdmPageRequest thePageRequest) { protected IBaseParameters parametersFromMdmLinks(Page<MdmLinkJson> theMdmLinkStream, boolean includeResultAndSource,
ServletRequestDetails theServletRequestDetails, MdmPageRequest thePageRequest) {
IBaseParameters retval = ParametersUtil.newInstance(myFhirContext); IBaseParameters retval = ParametersUtil.newInstance(myFhirContext);
addPagingParameters(retval, theMdmLinkStream, theServletRequestDetails, thePageRequest); addPagingParameters(retval, theMdmLinkStream, theServletRequestDetails, thePageRequest);
theMdmLinkStream.getContent().forEach(mdmLink -> { theMdmLinkStream.getContent().forEach(mdmLink -> {
@ -129,6 +130,8 @@ public abstract class BaseMdmProvider {
ParametersUtil.addPartBoolean(myFhirContext, resultPart, "eidMatch", mdmLink.getEidMatch()); ParametersUtil.addPartBoolean(myFhirContext, resultPart, "eidMatch", mdmLink.getEidMatch());
ParametersUtil.addPartBoolean(myFhirContext, resultPart, "hadToCreateNewResource", mdmLink.getLinkCreatedNewResource()); ParametersUtil.addPartBoolean(myFhirContext, resultPart, "hadToCreateNewResource", mdmLink.getLinkCreatedNewResource());
ParametersUtil.addPartDecimal(myFhirContext, resultPart, "score", mdmLink.getScore()); ParametersUtil.addPartDecimal(myFhirContext, resultPart, "score", mdmLink.getScore());
ParametersUtil.addPartDecimal(myFhirContext, resultPart, "linkCreated", (double) mdmLink.getCreated().getTime());
ParametersUtil.addPartDecimal(myFhirContext, resultPart, "linkUpdated", (double) mdmLink.getUpdated().getTime());
} }
}); });
return retval; return retval;

View File

@ -182,25 +182,30 @@ public class MdmProviderDstu3Plus extends BaseMdmProvider {
@OperationParam(name = ProviderConstants.MDM_QUERY_LINKS_LINK_SOURCE, min = 0, max = 1, typeName = "string") @OperationParam(name = ProviderConstants.MDM_QUERY_LINKS_LINK_SOURCE, min = 0, max = 1, typeName = "string")
IPrimitiveType<String> theLinkSource, IPrimitiveType<String> theLinkSource,
@Description(formalDefinition = "Results from this method are returned across multiple pages. This parameter controls the offset when fetching a page.") @Description(value = "Results from this method are returned across multiple pages. This parameter controls the offset when fetching a page.")
@OperationParam(name = PARAM_OFFSET, min = 0, max = 1, typeName = "integer") @OperationParam(name = PARAM_OFFSET, min = 0, max = 1, typeName = "integer")
IPrimitiveType<Integer> theOffset, IPrimitiveType<Integer> theOffset,
@Description(formalDefinition = "Results from this method are returned across multiple pages. This parameter controls the size of those pages.")
@Description(value = "Results from this method are returned across multiple pages. This parameter controls the size of those pages.")
@OperationParam(name = Constants.PARAM_COUNT, min = 0, max = 1, typeName = "integer") @OperationParam(name = Constants.PARAM_COUNT, min = 0, max = 1, typeName = "integer")
IPrimitiveType<Integer> theCount, IPrimitiveType<Integer> theCount,
@OperationParam(name = Constants.PARAM_SORT, min = 0, max = 1, typeName = "string")
IPrimitiveType<String> theSort,
ServletRequestDetails theRequestDetails, ServletRequestDetails theRequestDetails,
@OperationParam(name = ProviderConstants.MDM_RESOURCE_TYPE, min = 0, max = 1, typeName = "string") IPrimitiveType<String> theResourceType @OperationParam(name = ProviderConstants.MDM_RESOURCE_TYPE, min = 0, max = 1, typeName = "string") IPrimitiveType<String> theResourceType
) { ) {
MdmPageRequest mdmPageRequest = new MdmPageRequest(theOffset, theCount, DEFAULT_PAGE_SIZE, MAX_PAGE_SIZE); MdmPageRequest mdmPageRequest = new MdmPageRequest(theOffset, theCount, DEFAULT_PAGE_SIZE, MAX_PAGE_SIZE);
MdmTransactionContext mdmContext = createMdmContext(theRequestDetails, MdmTransactionContext.OperationType.QUERY_LINKS, MdmTransactionContext mdmContext = createMdmContext(theRequestDetails, MdmTransactionContext.OperationType.QUERY_LINKS,
getResourceType(ProviderConstants.MDM_QUERY_LINKS_GOLDEN_RESOURCE_ID, theGoldenResourceId, theResourceType)); getResourceType(ProviderConstants.MDM_QUERY_LINKS_GOLDEN_RESOURCE_ID, theGoldenResourceId, theResourceType));
MdmQuerySearchParameters mdmQuerySearchParameters = new MdmQuerySearchParameters(); MdmQuerySearchParameters mdmQuerySearchParameters = new MdmQuerySearchParameters(mdmPageRequest)
mdmQuerySearchParameters.setGoldenResourceId(extractStringOrNull(theGoldenResourceId)); .setGoldenResourceId(extractStringOrNull(theGoldenResourceId))
mdmQuerySearchParameters.setSourceId(extractStringOrNull(theResourceId)); .setSourceId(extractStringOrNull(theResourceId))
mdmQuerySearchParameters.setLinkSource(extractStringOrNull(theLinkSource)); .setLinkSource(extractStringOrNull(theLinkSource))
mdmQuerySearchParameters.setMatchResult(extractStringOrNull(theMatchResult)); .setMatchResult(extractStringOrNull(theMatchResult))
mdmQuerySearchParameters.setPageRequest(mdmPageRequest); .setResourceType(extractStringOrNull(theResourceType))
mdmQuerySearchParameters.setResourceType(extractStringOrNull(theResourceType)); .setSort(extractStringOrNull(theSort));
Page<MdmLinkJson> mdmLinkJson = myMdmControllerSvc.queryLinks(mdmQuerySearchParameters, mdmContext, theRequestDetails); Page<MdmLinkJson> mdmLinkJson = myMdmControllerSvc.queryLinks(mdmQuerySearchParameters, mdmContext, theRequestDetails);
return parametersFromMdmLinks(mdmLinkJson, true, theRequestDetails, mdmPageRequest); return parametersFromMdmLinks(mdmLinkJson, true, theRequestDetails, mdmPageRequest);

View File

@ -20,13 +20,12 @@ class MdmQuerySearchParametersTest {
@Test @Test
public void testMdmQuerySearchParameters() { public void testMdmQuerySearchParameters() {
MdmQuerySearchParameters params = new MdmQuerySearchParameters(); MdmQuerySearchParameters params = new MdmQuerySearchParameters(PAGE_REQUEST);
params.setGoldenResourceId(GOLDEN_RESOURCE_ID); params.setGoldenResourceId(GOLDEN_RESOURCE_ID);
params.setSourceId(SOURCE_ID); params.setSourceId(SOURCE_ID);
params.setMatchResult(MATCH_RESULT); params.setMatchResult(MATCH_RESULT);
params.setLinkSource(LINK_SOURCE); params.setLinkSource(LINK_SOURCE);
params.setPartitionIds(PARTITION_ID); params.setPartitionIds(PARTITION_ID);
params.setPageRequest(PAGE_REQUEST);
params.setResourceType(RESOURCE_TYPE); params.setResourceType(RESOURCE_TYPE);
assertEquals(GOLDEN_RESOURCE_ID, params.getGoldenResourceId().getValueAsString()); assertEquals(GOLDEN_RESOURCE_ID, params.getGoldenResourceId().getValueAsString());
assertEquals(SOURCE_ID, params.getSourceId().getValueAsString()); assertEquals(SOURCE_ID, params.getSourceId().getValueAsString());