Compare commits

...

5 Commits

Author SHA1 Message Date
Jens Kristian Villadsen af0290f0fd
Merge e2a8693a3d into fb7571185a 2024-09-27 14:10:32 +02:00
volodymyr-korzh fb7571185a
Composite unique search parameter with dateTime component does not reject resource duplication (#6318)
* Composite Unique Search Parameter with dateTime component does not reject resource duplication - failing tests

* Composite Unique Search Parameter with dateTime component does not reject resource duplication - implementation

* Composite Unique Search Parameter with dateTime component does not reject resource duplication - changelog and test fixes
2024-09-26 09:18:35 -06:00
Tadgh 919e2d2405
Attribution for javamail and API bump (#6319) 2024-09-26 14:56:49 +00:00
Jens Kristian Villadsen e2a8693a3d
Merge branch 'hapifhir:master' into serialization-bug 2023-07-23 12:43:45 +02:00
Jens Kristian Villadsen c15bfec080 Added test to show a bug 2023-07-04 00:55:42 +02:00
10 changed files with 836 additions and 59 deletions

View File

@ -0,0 +1,5 @@
---
type: change
issue: 6261
title: "Upgrading to Jakarta had caused a problem with the version of java-simple-mail that was in use. This has been updated to be conformant
with the Jakarta Mail APIs. Thanks to Thomas Papke(@thopap) for the contribution!"

View File

@ -0,0 +1,7 @@
---
type: fix
issue: 6317
title: "Previously, defining a unique combo Search Parameter with the DateTime component and submitting multiple
resources with the same dateTime element (e.g. Observation.effectiveDateTime) resulted in duplicate resource creation.
This has been fixed."

View File

@ -1972,7 +1972,7 @@ public class SearchBuilder implements ISearchBuilder<JpaPid> {
* The loop allows us to create multiple combo index joins if there * The loop allows us to create multiple combo index joins if there
* are multiple AND expressions for the related parameters. * are multiple AND expressions for the related parameters.
*/ */
while (validateParamValuesAreValidForComboParam(theRequest, theParams, comboParamNames)) { while (validateParamValuesAreValidForComboParam(theRequest, theParams, comboParamNames, comboParam)) {
applyComboSearchParam(theQueryStack, theParams, theRequest, comboParamNames, comboParam); applyComboSearchParam(theQueryStack, theParams, theRequest, comboParamNames, comboParam);
} }
} }
@ -2068,7 +2068,10 @@ public class SearchBuilder implements ISearchBuilder<JpaPid> {
* (e.g. <code>?date=gt2024-02-01</code>), etc. * (e.g. <code>?date=gt2024-02-01</code>), etc.
*/ */
private boolean validateParamValuesAreValidForComboParam( private boolean validateParamValuesAreValidForComboParam(
RequestDetails theRequest, @Nonnull SearchParameterMap theParams, List<String> theComboParamNames) { RequestDetails theRequest,
@Nonnull SearchParameterMap theParams,
List<String> theComboParamNames,
RuntimeSearchParam theComboParam) {
boolean paramValuesAreValidForCombo = true; boolean paramValuesAreValidForCombo = true;
List<List<IQueryParameterType>> paramOrValues = new ArrayList<>(theComboParamNames.size()); List<List<IQueryParameterType>> paramOrValues = new ArrayList<>(theComboParamNames.size());
@ -2129,6 +2132,19 @@ public class SearchBuilder implements ISearchBuilder<JpaPid> {
break; break;
} }
} }
// Date params are not eligible for using composite unique index
// as index could contain date with different precision (e.g. DAY, SECOND)
if (nextParamDef.getParamType() == RestSearchParameterTypeEnum.DATE
&& theComboParam.getComboSearchParamType() == ComboSearchParamType.UNIQUE) {
ourLog.debug(
"Search with params {} is not a candidate for combo searching - "
+ "Unique combo search parameter '{}' has DATE type",
theComboParamNames,
nextParamName);
paramValuesAreValidForCombo = false;
break;
}
} }
if (CartesianProductUtil.calculateCartesianProductSize(paramOrValues) > 500) { if (CartesianProductUtil.calculateCartesianProductSize(paramOrValues) > 500) {

View File

@ -566,7 +566,8 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
for (BaseResourceIndexedSearchParam nextParam : paramsListForCompositePart) { for (BaseResourceIndexedSearchParam nextParam : paramsListForCompositePart) {
IQueryParameterType nextParamAsClientParam = nextParam.toQueryParameterType(); IQueryParameterType nextParamAsClientParam = nextParam.toQueryParameterType();
if (nextParamAsClientParam instanceof DateParam) { if (theParam.getComboSearchParamType() == ComboSearchParamType.NON_UNIQUE
&& nextParamAsClientParam instanceof DateParam) {
DateParam date = (DateParam) nextParamAsClientParam; DateParam date = (DateParam) nextParamAsClientParam;
if (date.getPrecision() != TemporalPrecisionEnum.DAY) { if (date.getPrecision() != TemporalPrecisionEnum.DAY) {
continue; continue;

View File

@ -135,10 +135,10 @@ public abstract class BasePartitioningR4Test extends BaseJpaR4SystemTest {
addCreateDefaultPartition(); addCreateDefaultPartition();
addReadDefaultPartition(); // one for search param validation addReadDefaultPartition(); // one for search param validation
SearchParameter sp = new SearchParameter(); SearchParameter sp = new SearchParameter();
sp.setId("SearchParameter/patient-birthdate"); sp.setId("SearchParameter/patient-gender");
sp.setType(Enumerations.SearchParamType.DATE); sp.setType(Enumerations.SearchParamType.TOKEN);
sp.setCode("birthdate"); sp.setCode("gender");
sp.setExpression("Patient.birthDate"); sp.setExpression("Patient.gender");
sp.setStatus(Enumerations.PublicationStatus.ACTIVE); sp.setStatus(Enumerations.PublicationStatus.ACTIVE);
sp.addBase("Patient"); sp.addBase("Patient");
mySearchParameterDao.update(sp, mySrd); mySearchParameterDao.update(sp, mySrd);
@ -156,13 +156,13 @@ public abstract class BasePartitioningR4Test extends BaseJpaR4SystemTest {
addCreateDefaultPartition(); addCreateDefaultPartition();
sp = new SearchParameter(); sp = new SearchParameter();
sp.setId("SearchParameter/patient-birthdate-unique"); sp.setId("SearchParameter/patient-gender-family-unique");
sp.setType(Enumerations.SearchParamType.COMPOSITE); sp.setType(Enumerations.SearchParamType.COMPOSITE);
sp.setStatus(Enumerations.PublicationStatus.ACTIVE); sp.setStatus(Enumerations.PublicationStatus.ACTIVE);
sp.addBase("Patient"); sp.addBase("Patient");
sp.addComponent() sp.addComponent()
.setExpression("Patient") .setExpression("Patient")
.setDefinition("SearchParameter/patient-birthdate"); .setDefinition("SearchParameter/patient-gender");
sp.addComponent() sp.addComponent()
.setExpression("Patient") .setExpression("Patient")
.setDefinition("SearchParameter/patient-family"); .setDefinition("SearchParameter/patient-family");

View File

@ -13,10 +13,10 @@ import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.jpa.searchparam.util.JpaParamUtil; import ca.uhn.fhir.jpa.searchparam.util.JpaParamUtil;
import ca.uhn.fhir.rest.api.server.IBundleProvider; import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.param.DateAndListParam;
import ca.uhn.fhir.rest.param.DateOrListParam;
import ca.uhn.fhir.rest.param.DateParam; import ca.uhn.fhir.rest.param.DateParam;
import ca.uhn.fhir.rest.param.ReferenceParam; import ca.uhn.fhir.rest.param.ReferenceParam;
import ca.uhn.fhir.rest.param.StringOrListParam;
import ca.uhn.fhir.rest.param.StringParam;
import ca.uhn.fhir.rest.param.TokenAndListParam; import ca.uhn.fhir.rest.param.TokenAndListParam;
import ca.uhn.fhir.rest.param.TokenParam; import ca.uhn.fhir.rest.param.TokenParam;
import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException; import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException;
@ -33,6 +33,7 @@ import org.hl7.fhir.r4.model.DateType;
import org.hl7.fhir.r4.model.Encounter; import org.hl7.fhir.r4.model.Encounter;
import org.hl7.fhir.r4.model.Enumerations; import org.hl7.fhir.r4.model.Enumerations;
import org.hl7.fhir.r4.model.Enumerations.PublicationStatus; import org.hl7.fhir.r4.model.Enumerations.PublicationStatus;
import org.hl7.fhir.r4.model.HumanName;
import org.hl7.fhir.r4.model.IdType; import org.hl7.fhir.r4.model.IdType;
import org.hl7.fhir.r4.model.Observation; import org.hl7.fhir.r4.model.Observation;
import org.hl7.fhir.r4.model.Organization; import org.hl7.fhir.r4.model.Organization;
@ -115,6 +116,46 @@ public class FhirResourceDaoR4ComboUniqueParamTest extends BaseComboParamsR4Test
myMessages.clear(); myMessages.clear();
} }
private void createUniqueGenderFamilyComboSp() {
SearchParameter sp = new SearchParameter();
sp.setId("SearchParameter/patient-gender");
sp.setType(Enumerations.SearchParamType.TOKEN);
sp.setCode("gender");
sp.setExpression("Patient.gender");
sp.setStatus(PublicationStatus.ACTIVE);
sp.addBase("Patient");
mySearchParameterDao.update(sp, mySrd);
sp = new SearchParameter();
sp.setId("SearchParameter/patient-family");
sp.setType(Enumerations.SearchParamType.STRING);
sp.setCode("family");
sp.setExpression("Patient.name.family");
sp.setStatus(Enumerations.PublicationStatus.ACTIVE);
sp.addBase("Patient");
mySearchParameterDao.update(sp, mySrd);
sp = new SearchParameter();
sp.setId("SearchParameter/patient-gender-family");
sp.setType(Enumerations.SearchParamType.COMPOSITE);
sp.setStatus(PublicationStatus.ACTIVE);
sp.addBase("Patient");
sp.addComponent()
.setExpression("Patient")
.setDefinition("SearchParameter/patient-gender");
sp.addComponent()
.setExpression("Patient")
.setDefinition("SearchParameter/patient-family");
sp.addExtension()
.setUrl(HapiExtensions.EXT_SP_UNIQUE)
.setValue(new BooleanType(true));
mySearchParameterDao.update(sp, mySrd);
mySearchParamRegistry.forceRefresh();
myMessages.clear();
}
private void createUniqueIndexCoverageBeneficiary() { private void createUniqueIndexCoverageBeneficiary() {
SearchParameter sp = new SearchParameter(); SearchParameter sp = new SearchParameter();
sp.setId("SearchParameter/coverage-beneficiary"); sp.setId("SearchParameter/coverage-beneficiary");
@ -276,6 +317,45 @@ public class FhirResourceDaoR4ComboUniqueParamTest extends BaseComboParamsR4Test
mySearchParamRegistry.forceRefresh(); mySearchParamRegistry.forceRefresh();
} }
private void createUniqueObservationDateCode() {
SearchParameter sp = new SearchParameter();
sp.setId("SearchParameter/obs-effective");
sp.setType(Enumerations.SearchParamType.DATE);
sp.setCode("date");
sp.setExpression("Observation.effective");
sp.setStatus(PublicationStatus.ACTIVE);
sp.addBase("Observation");
mySearchParameterDao.update(sp, mySrd);
sp = new SearchParameter();
sp.setId("SearchParameter/obs-code");
sp.setType(Enumerations.SearchParamType.TOKEN);
sp.setCode("code");
sp.setExpression("Observation.code");
sp.setStatus(PublicationStatus.ACTIVE);
sp.addBase("Observation");
mySearchParameterDao.update(sp, mySrd);
sp = new SearchParameter();
sp.setId("SearchParameter/observation-date-code");
sp.setType(Enumerations.SearchParamType.COMPOSITE);
sp.setStatus(PublicationStatus.ACTIVE);
sp.addBase("Observation");
sp.setExpression("Observation.code");
sp.addComponent()
.setExpression("Observation")
.setDefinition("SearchParameter/obs-effective");
sp.addComponent()
.setExpression("Observation")
.setDefinition("SearchParameter/obs-code");
sp.addExtension()
.setUrl(HapiExtensions.EXT_SP_UNIQUE)
.setValue(new BooleanType(true));
mySearchParameterDao.update(sp, mySrd);
mySearchParamRegistry.forceRefresh();
}
private void createUniqueObservationSubjectDateCode() { private void createUniqueObservationSubjectDateCode() {
SearchParameter sp = new SearchParameter(); SearchParameter sp = new SearchParameter();
sp.setId("SearchParameter/obs-subject"); sp.setId("SearchParameter/obs-subject");
@ -471,11 +551,11 @@ public class FhirResourceDaoR4ComboUniqueParamTest extends BaseComboParamsR4Test
public void testDoubleMatchingOnAnd_Search_TwoAndOrValues() { public void testDoubleMatchingOnAnd_Search_TwoAndOrValues() {
myStorageSettings.setUniqueIndexesCheckedBeforeSave(false); myStorageSettings.setUniqueIndexesCheckedBeforeSave(false);
createUniqueBirthdateAndGenderSps(); createUniqueGenderFamilyComboSp();
Patient pt1 = new Patient(); Patient pt1 = new Patient();
pt1.setGender(Enumerations.AdministrativeGender.MALE); pt1.setGender(Enumerations.AdministrativeGender.MALE);
pt1.setBirthDateElement(new DateType("2011-01-01")); pt1.getName().add(new HumanName().setFamily("Family1"));
String id1 = myPatientDao.create(pt1, mySrd).getId().toUnqualifiedVersionless().getValue(); String id1 = myPatientDao.create(pt1, mySrd).getId().toUnqualifiedVersionless().getValue();
// Two OR values on the same resource - Currently composite SPs don't work for this // Two OR values on the same resource - Currently composite SPs don't work for this
@ -484,17 +564,22 @@ public class FhirResourceDaoR4ComboUniqueParamTest extends BaseComboParamsR4Test
sp.setLoadSynchronous(true); sp.setLoadSynchronous(true);
sp.add(Patient.SP_GENDER, sp.add(Patient.SP_GENDER,
new TokenAndListParam() new TokenAndListParam()
.addAnd(new TokenParam("http://hl7.org/fhir/administrative-gender","male"), new TokenParam( "http://hl7.org/fhir/administrative-gender","female")) .addAnd(new TokenParam("http://hl7.org/fhir/administrative-gender","male"),
new TokenParam( "http://hl7.org/fhir/administrative-gender","female"))
); );
sp.add(Patient.SP_BIRTHDATE, sp.add(Patient.SP_FAMILY,
new DateAndListParam() new StringOrListParam()
.addAnd(new DateParam("2011-01-01"), new DateParam( "2011-02-02")) .addOr(new StringParam("Family1")).addOr(new StringParam("Family2"))
); );
IBundleProvider outcome = myPatientDao.search(sp, mySrd); IBundleProvider outcome = myPatientDao.search(sp, mySrd);
myCaptureQueriesListener.logFirstSelectQueryForCurrentThread(); myCaptureQueriesListener.logFirstSelectQueryForCurrentThread();
assertThat(toUnqualifiedVersionlessIdValues(outcome)).containsExactlyInAnyOrder(id1); assertThat(toUnqualifiedVersionlessIdValues(outcome)).containsExactlyInAnyOrder(id1);
String unformattedSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, false); String unformattedSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, false);
assertEquals("SELECT t0.RES_ID FROM HFJ_IDX_CMP_STRING_UNIQ t0 WHERE (t0.IDX_STRING IN ('Patient?birthdate=2011-01-01&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cfemale','Patient?birthdate=2011-01-01&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cmale','Patient?birthdate=2011-02-02&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cfemale','Patient?birthdate=2011-02-02&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cmale') )", unformattedSql); assertEquals("SELECT t0.RES_ID FROM HFJ_IDX_CMP_STRING_UNIQ t0 WHERE (t0.IDX_STRING IN (" +
"'Patient?family=Family1&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cfemale'," +
"'Patient?family=Family1&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cmale'," +
"'Patient?family=Family2&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cfemale'," +
"'Patient?family=Family2&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cmale') )", unformattedSql);
} }
@ -1167,16 +1252,16 @@ public class FhirResourceDaoR4ComboUniqueParamTest extends BaseComboParamsR4Test
@Test @Test
public void testOrQuery() { public void testOrQuery() {
myStorageSettings.setAdvancedHSearchIndexing(false); myStorageSettings.setAdvancedHSearchIndexing(false);
createUniqueBirthdateAndGenderSps(); createUniqueGenderFamilyComboSp();
Patient pt1 = new Patient(); Patient pt1 = new Patient();
pt1.setGender(Enumerations.AdministrativeGender.MALE); pt1.setGender(Enumerations.AdministrativeGender.MALE);
pt1.setBirthDateElement(new DateType("2011-01-01")); pt1.getName().add(new HumanName().setFamily("Family1"));
IIdType id1 = myPatientDao.create(pt1, mySrd).getId().toUnqualifiedVersionless(); IIdType id1 = myPatientDao.create(pt1, mySrd).getId().toUnqualifiedVersionless();
Patient pt2 = new Patient(); Patient pt2 = new Patient();
pt2.setGender(Enumerations.AdministrativeGender.MALE); pt2.setGender(Enumerations.AdministrativeGender.MALE);
pt2.setBirthDateElement(new DateType("2011-01-02")); pt2.getName().add(new HumanName().setFamily("Family2"));
IIdType id2 = myPatientDao.create(pt2, mySrd).getId().toUnqualifiedVersionless(); IIdType id2 = myPatientDao.create(pt2, mySrd).getId().toUnqualifiedVersionless();
myCaptureQueriesListener.clear(); myCaptureQueriesListener.clear();
@ -1184,16 +1269,21 @@ public class FhirResourceDaoR4ComboUniqueParamTest extends BaseComboParamsR4Test
SearchParameterMap params = new SearchParameterMap(); SearchParameterMap params = new SearchParameterMap();
params.setLoadSynchronousUpTo(100); params.setLoadSynchronousUpTo(100);
params.add("gender", new TokenParam("http://hl7.org/fhir/administrative-gender", "male")); params.add("gender", new TokenParam("http://hl7.org/fhir/administrative-gender", "male"));
params.add("birthdate", new DateOrListParam().addOr(new DateParam("2011-01-01")).addOr(new DateParam("2011-01-02"))); params.add("family", new StringOrListParam()
.addOr(new StringParam("Family1")).addOr(new StringParam("Family2")));
myCaptureQueriesListener.clear(); myCaptureQueriesListener.clear();
IBundleProvider results = myPatientDao.search(params, mySrd); IBundleProvider results = myPatientDao.search(params, mySrd);
myCaptureQueriesListener.logFirstSelectQueryForCurrentThread(); myCaptureQueriesListener.logFirstSelectQueryForCurrentThread();
assertThat(toUnqualifiedVersionlessIdValues(results)).containsExactlyInAnyOrder(id1.getValue(), id2.getValue()); assertThat(toUnqualifiedVersionlessIdValues(results)).containsExactlyInAnyOrder(id1.getValue(), id2.getValue());
assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false)) assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false))
.contains("SELECT t0.RES_ID FROM HFJ_IDX_CMP_STRING_UNIQ t0 WHERE (t0.IDX_STRING IN ('Patient?birthdate=2011-01-01&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cmale','Patient?birthdate=2011-01-02&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cmale') )"); .contains("SELECT t0.RES_ID FROM HFJ_IDX_CMP_STRING_UNIQ t0 WHERE (t0.IDX_STRING IN " +
"('Patient?family=Family1&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cmale'," +
"'Patient?family=Family2&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cmale') )");
logCapturedMessages(); logCapturedMessages();
assertThat(myMessages.toString()).contains("Using UNIQUE index(es) for query for search: [Patient?birthdate=2011-01-01&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cmale, Patient?birthdate=2011-01-02&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cmale]"); assertThat(myMessages.toString()).contains("Using UNIQUE index(es) for query for search: " +
"[Patient?family=Family1&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cmale, " +
"Patient?family=Family2&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cmale]");
myMessages.clear(); myMessages.clear();
} }
@ -1201,16 +1291,16 @@ public class FhirResourceDaoR4ComboUniqueParamTest extends BaseComboParamsR4Test
@Test @Test
public void testSearchSynchronousUsingUniqueComposite() { public void testSearchSynchronousUsingUniqueComposite() {
myStorageSettings.setAdvancedHSearchIndexing(false); myStorageSettings.setAdvancedHSearchIndexing(false);
createUniqueBirthdateAndGenderSps(); createUniqueGenderFamilyComboSp();
Patient pt1 = new Patient(); Patient pt1 = new Patient();
pt1.setGender(Enumerations.AdministrativeGender.MALE); pt1.setGender(Enumerations.AdministrativeGender.MALE);
pt1.setBirthDateElement(new DateType("2011-01-01")); pt1.getName().add(new HumanName().setFamily("Family1"));
IIdType id1 = myPatientDao.create(pt1, mySrd).getId().toUnqualifiedVersionless(); IIdType id1 = myPatientDao.create(pt1, mySrd).getId().toUnqualifiedVersionless();
Patient pt2 = new Patient(); Patient pt2 = new Patient();
pt2.setGender(Enumerations.AdministrativeGender.MALE); pt2.setGender(Enumerations.AdministrativeGender.MALE);
pt2.setBirthDateElement(new DateType("2011-01-02")); pt2.getName().add(new HumanName().setFamily("Family2"));
myPatientDao.create(pt2, mySrd).getId().toUnqualifiedVersionless(); myPatientDao.create(pt2, mySrd).getId().toUnqualifiedVersionless();
myCaptureQueriesListener.clear(); myCaptureQueriesListener.clear();
@ -1218,13 +1308,14 @@ public class FhirResourceDaoR4ComboUniqueParamTest extends BaseComboParamsR4Test
SearchParameterMap params = new SearchParameterMap(); SearchParameterMap params = new SearchParameterMap();
params.setLoadSynchronousUpTo(100); params.setLoadSynchronousUpTo(100);
params.add("gender", new TokenParam("http://hl7.org/fhir/administrative-gender", "male")); params.add("gender", new TokenParam("http://hl7.org/fhir/administrative-gender", "male"));
params.add("birthdate", new DateParam("2011-01-01")); params.add("family", new StringParam("Family1"));
IBundleProvider results = myPatientDao.search(params, mySrd); IBundleProvider results = myPatientDao.search(params, mySrd);
myCaptureQueriesListener.logFirstSelectQueryForCurrentThread(); myCaptureQueriesListener.logFirstSelectQueryForCurrentThread();
assertThat(toUnqualifiedVersionlessIdValues(results)).containsExactlyInAnyOrder(id1.getValue()); assertThat(toUnqualifiedVersionlessIdValues(results)).containsExactlyInAnyOrder(id1.getValue());
logCapturedMessages(); logCapturedMessages();
assertThat(myMessages.toString()).contains("Using UNIQUE index(es) for query for search: Patient?birthdate=2011-01-01&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cmale"); assertThat(myMessages.toString()).contains("Using UNIQUE index(es) for query for search: " +
"Patient?family=Family1&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cmale");
myMessages.clear(); myMessages.clear();
} }
@ -1232,33 +1323,34 @@ public class FhirResourceDaoR4ComboUniqueParamTest extends BaseComboParamsR4Test
@Test @Test
public void testSearchUsingUniqueComposite() { public void testSearchUsingUniqueComposite() {
createUniqueBirthdateAndGenderSps(); createUniqueGenderFamilyComboSp();
Patient pt1 = new Patient(); Patient pt1 = new Patient();
pt1.setGender(Enumerations.AdministrativeGender.MALE); pt1.setGender(Enumerations.AdministrativeGender.MALE);
pt1.setBirthDateElement(new DateType("2011-01-01")); pt1.getName().add(new HumanName().setFamily("Family1"));
String id1 = myPatientDao.create(pt1, mySrd).getId().toUnqualifiedVersionless().getValue(); String id1 = myPatientDao.create(pt1, mySrd).getId().toUnqualifiedVersionless().getValue();
Patient pt2 = new Patient(); Patient pt2 = new Patient();
pt2.setGender(Enumerations.AdministrativeGender.MALE); pt2.setGender(Enumerations.AdministrativeGender.MALE);
pt2.setBirthDateElement(new DateType("2011-01-02")); pt2.getName().add(new HumanName().setFamily("Family2"));
myPatientDao.create(pt2, mySrd).getId().toUnqualifiedVersionless(); myPatientDao.create(pt2, mySrd).getId().toUnqualifiedVersionless();
myMessages.clear(); myMessages.clear();
SearchParameterMap params = new SearchParameterMap(); SearchParameterMap params = new SearchParameterMap();
params.add("gender", new TokenParam("http://hl7.org/fhir/administrative-gender", "male")); params.add("gender", new TokenParam("http://hl7.org/fhir/administrative-gender", "male"));
params.add("birthdate", new DateParam("2011-01-01")); params.add("family", new StringParam("Family1"));
IBundleProvider results = myPatientDao.search(params, mySrd); IBundleProvider results = myPatientDao.search(params, mySrd);
String searchId = results.getUuid(); String searchId = results.getUuid();
assertThat(toUnqualifiedVersionlessIdValues(results)).containsExactlyInAnyOrder(id1); assertThat(toUnqualifiedVersionlessIdValues(results)).containsExactlyInAnyOrder(id1);
logCapturedMessages(); logCapturedMessages();
assertThat(myMessages.toString()).contains("Using UNIQUE index(es) for query for search: Patient?birthdate=2011-01-01&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cmale"); assertThat(myMessages.toString()).contains("Using UNIQUE index(es) for query for search: " +
"Patient?family=Family1&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cmale");
myMessages.clear(); myMessages.clear();
// Other order // Other order
myMessages.clear(); myMessages.clear();
params = new SearchParameterMap(); params = new SearchParameterMap();
params.add("birthdate", new DateParam("2011-01-01")); params.add("family", new StringParam("Family1"));
params.add("gender", new TokenParam("http://hl7.org/fhir/administrative-gender", "male")); params.add("gender", new TokenParam("http://hl7.org/fhir/administrative-gender", "male"));
results = myPatientDao.search(params, mySrd); results = myPatientDao.search(params, mySrd);
assertEquals(searchId, results.getUuid()); assertEquals(searchId, results.getUuid());
@ -1272,16 +1364,17 @@ public class FhirResourceDaoR4ComboUniqueParamTest extends BaseComboParamsR4Test
myMessages.clear(); myMessages.clear();
params = new SearchParameterMap(); params = new SearchParameterMap();
params.add("gender", new TokenParam("http://hl7.org/fhir/administrative-gender", "male")); params.add("gender", new TokenParam("http://hl7.org/fhir/administrative-gender", "male"));
params.add("birthdate", new DateParam("2011-01-03")); params.add("family", new StringParam("Family3"));
results = myPatientDao.search(params, mySrd); results = myPatientDao.search(params, mySrd);
assertThat(toUnqualifiedVersionlessIdValues(results)).isEmpty(); assertThat(toUnqualifiedVersionlessIdValues(results)).isEmpty();
logCapturedMessages(); logCapturedMessages();
assertThat(myMessages.toString()).contains("Using UNIQUE index(es) for query for search: Patient?birthdate=2011-01-03&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cmale"); assertThat(myMessages.toString()).contains("Using UNIQUE index(es) for query for search: " +
"Patient?family=Family3&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cmale");
myMessages.clear(); myMessages.clear();
myMessages.clear(); myMessages.clear();
params = new SearchParameterMap(); params = new SearchParameterMap();
params.add("birthdate", new DateParam("2011-01-03")); params.add("family", new StringParam("Family3"));
results = myPatientDao.search(params, mySrd); results = myPatientDao.search(params, mySrd);
assertThat(toUnqualifiedVersionlessIdValues(results)).isEmpty(); assertThat(toUnqualifiedVersionlessIdValues(results)).isEmpty();
// STANDARD QUERY // STANDARD QUERY
@ -1666,7 +1759,7 @@ public class FhirResourceDaoR4ComboUniqueParamTest extends BaseComboParamsR4Test
} }
@Test @Test
public void testDuplicateUniqueValuesAreRejected() { public void testDuplicateUniqueValuesWithDateAreRejected() {
createUniqueBirthdateAndGenderSps(); createUniqueBirthdateAndGenderSps();
Patient pt1 = new Patient(); Patient pt1 = new Patient();
@ -1699,13 +1792,75 @@ public class FhirResourceDaoR4ComboUniqueParamTest extends BaseComboParamsR4Test
} }
@Test @Test
public void testReplaceOneWithAnother() { public void testDuplicateUniqueValuesWithDateTimeAreRejected() {
myStorageSettings.setAdvancedHSearchIndexing(false); createUniqueObservationDateCode();
Observation obs = new Observation();
obs.getCode().addCoding().setSystem("foo").setCode("bar");
obs.setEffective(new DateTimeType("2017-10-10T00:00:00"));
myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
try {
myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
fail();
} catch (ResourceVersionConflictException e) {
assertThat(e.getMessage())
.contains("new unique index created by SearchParameter/observation-date-code");
}
}
@Test
public void testUniqueComboSearchWithDateNotUsingUniqueIndex() {
createUniqueBirthdateAndGenderSps(); createUniqueBirthdateAndGenderSps();
Patient pt1 = new Patient(); Patient pt1 = new Patient();
pt1.setGender(Enumerations.AdministrativeGender.MALE); pt1.setGender(Enumerations.AdministrativeGender.MALE);
pt1.setBirthDateElement(new DateType("2011-01-01")); pt1.setBirthDateElement(new DateType("2011-01-01"));
String pId = myPatientDao.create(pt1, mySrd).getId().toUnqualifiedVersionless().getValue();
myCaptureQueriesListener.clear();
SearchParameterMap params = new SearchParameterMap();
params.setLoadSynchronousUpTo(100);
params.add("gender", new TokenParam("http://hl7.org/fhir/administrative-gender", "male"));
params.add("birthdate", new DateParam("2011-01-01"));
IBundleProvider results = myPatientDao.search(params, mySrd);
assertThat(toUnqualifiedVersionlessIdValues(results)).containsExactlyInAnyOrder(pId);
myCaptureQueriesListener.logFirstSelectQueryForCurrentThread();
String unformattedSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, false);
assertThat(unformattedSql).doesNotContain("HFJ_IDX_CMP_STRING_UNIQ");
}
@Test
public void testUniqueComboSearchWithDateTimeNotUsingUniqueIndex() {
createUniqueObservationDateCode();
Observation obs = new Observation();
obs.getCode().addCoding().setSystem("foo").setCode("bar");
obs.setEffective(new DateTimeType("2017-10-10T00:00:00"));
String obsId = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless().getValue();
myCaptureQueriesListener.clear();
SearchParameterMap params = new SearchParameterMap();
params.setLoadSynchronousUpTo(100);
params.add("code", new TokenParam("foo", "bar"));
params.add("date", new DateParam("2017-10-10T00:00:00"));
IBundleProvider results = myObservationDao.search(params, mySrd);
assertThat(toUnqualifiedVersionlessIdValues(results)).containsExactlyInAnyOrder(obsId);
myCaptureQueriesListener.logFirstSelectQueryForCurrentThread();
String unformattedSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, false);
assertThat(unformattedSql).doesNotContain("HFJ_IDX_CMP_STRING_UNIQ");
}
@Test
public void testReplaceOneWithAnother() {
myStorageSettings.setAdvancedHSearchIndexing(false);
createUniqueGenderFamilyComboSp();
Patient pt1 = new Patient();
pt1.setGender(Enumerations.AdministrativeGender.MALE);
pt1.getName().add(new HumanName().setFamily("Family1"));
IIdType id1 = myPatientDao.create(pt1, mySrd).getId().toUnqualified(); IIdType id1 = myPatientDao.create(pt1, mySrd).getId().toUnqualified();
assertNotNull(id1); assertNotNull(id1);
@ -1714,27 +1869,28 @@ public class FhirResourceDaoR4ComboUniqueParamTest extends BaseComboParamsR4Test
pt1 = new Patient(); pt1 = new Patient();
pt1.setId(id1); pt1.setId(id1);
pt1.setGender(Enumerations.AdministrativeGender.FEMALE); pt1.setGender(Enumerations.AdministrativeGender.FEMALE);
pt1.setBirthDateElement(new DateType("2011-01-01")); pt1.getName().add(new HumanName().setFamily("Family1"));
id1 = myPatientDao.update(pt1, mySrd).getId().toUnqualified(); id1 = myPatientDao.update(pt1, mySrd).getId().toUnqualified();
assertNotNull(id1); assertNotNull(id1);
assertEquals("2", id1.getVersionIdPart()); assertEquals("2", id1.getVersionIdPart());
Patient pt2 = new Patient(); Patient pt2 = new Patient();
pt2.setGender(Enumerations.AdministrativeGender.MALE); pt2.setGender(Enumerations.AdministrativeGender.MALE);
pt2.setBirthDateElement(new DateType("2011-01-01")); pt2.getName().add(new HumanName().setFamily("Family1"));
IIdType id2 = myPatientDao.create(pt2, mySrd).getId().toUnqualifiedVersionless(); IIdType id2 = myPatientDao.create(pt2, mySrd).getId().toUnqualifiedVersionless();
myMessages.clear(); myMessages.clear();
SearchParameterMap params = new SearchParameterMap(); SearchParameterMap params = new SearchParameterMap();
params.add("gender", new TokenParam("http://hl7.org/fhir/administrative-gender", "male")); params.add("gender", new TokenParam("http://hl7.org/fhir/administrative-gender", "male"));
params.add("birthdate", new DateParam("2011-01-01")); params.add("family", new StringParam("Family1"));
IBundleProvider results = myPatientDao.search(params, mySrd); IBundleProvider results = myPatientDao.search(params, mySrd);
String searchId = results.getUuid(); String searchId = results.getUuid();
assertThat(searchId).isNotBlank(); assertThat(searchId).isNotBlank();
assertThat(toUnqualifiedVersionlessIdValues(results)).containsExactlyInAnyOrder(id2.getValue()); assertThat(toUnqualifiedVersionlessIdValues(results)).containsExactlyInAnyOrder(id2.getValue());
logCapturedMessages(); logCapturedMessages();
assertThat(myMessages.toString()).contains("Using UNIQUE index(es) for query for search: Patient?birthdate=2011-01-01&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cmale"); assertThat(myMessages.toString()).contains("Using UNIQUE index(es) for query for search: " +
"Patient?family=Family1&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cmale");
myMessages.clear(); myMessages.clear();
} }

View File

@ -413,7 +413,7 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test {
p.getMeta().addTag("http://system", "code", "diisplay"); p.getMeta().addTag("http://system", "code", "diisplay");
p.addName().setFamily("FAM"); p.addName().setFamily("FAM");
p.addIdentifier().setSystem("system").setValue("value"); p.addIdentifier().setSystem("system").setValue("value");
p.setBirthDateElement(new DateType("2020-01-01")); p.setGender(Enumerations.AdministrativeGender.MALE);
p.getManagingOrganization().setReferenceElement(orgId); p.getManagingOrganization().setReferenceElement(orgId);
Long patientId = myPatientDao.create(p, mySrd).getId().getIdPartAsLong(); Long patientId = myPatientDao.create(p, mySrd).getId().getIdPartAsLong();
@ -502,7 +502,7 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test {
p.getMeta().addTag("http://system", "code", "diisplay"); p.getMeta().addTag("http://system", "code", "diisplay");
p.addName().setFamily("FAM"); p.addName().setFamily("FAM");
p.addIdentifier().setSystem("system").setValue("value"); p.addIdentifier().setSystem("system").setValue("value");
p.setBirthDate(new Date()); p.setGender(Enumerations.AdministrativeGender.MALE);
p.getManagingOrganization().setReferenceElement(orgId); p.getManagingOrganization().setReferenceElement(orgId);
Long patientId = myPatientDao.create(p, mySrd).getId().getIdPartAsLong(); Long patientId = myPatientDao.create(p, mySrd).getId().getIdPartAsLong();
@ -679,7 +679,7 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test {
p.getMeta().addTag("http://system", "code", "display"); p.getMeta().addTag("http://system", "code", "display");
p.addName().setFamily("FAM"); p.addName().setFamily("FAM");
p.addIdentifier().setSystem("system").setValue("value"); p.addIdentifier().setSystem("system").setValue("value");
p.setBirthDate(new Date()); p.setGender(Enumerations.AdministrativeGender.MALE);
p.getManagingOrganization().setReference(org.getId()); p.getManagingOrganization().setReference(org.getId());
input.addEntry() input.addEntry()
.setFullUrl(p.getId()) .setFullUrl(p.getId())
@ -2541,14 +2541,14 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test {
public void testSearch_UniqueParam_SearchAllPartitions() { public void testSearch_UniqueParam_SearchAllPartitions() {
createUniqueComboSp(); createUniqueComboSp();
IIdType id = createPatient(withPartition(1), withBirthdate("2020-01-01"), withFamily("FAM")); IIdType id = createPatient(withPartition(1), withGender("male"), withFamily("FAM"));
addReadAllPartitions(); addReadAllPartitions();
myCaptureQueriesListener.clear(); myCaptureQueriesListener.clear();
SearchParameterMap map = new SearchParameterMap(); SearchParameterMap map = new SearchParameterMap();
map.add(Patient.SP_FAMILY, new StringParam("FAM")); map.add(Patient.SP_FAMILY, new StringParam("FAM"));
map.add(Patient.SP_BIRTHDATE, new DateParam("2020-01-01")); map.add(Patient.SP_GENDER, new TokenParam(null, "male"));
map.setLoadSynchronous(true); map.setLoadSynchronous(true);
IBundleProvider results = myPatientDao.search(map, mySrd); IBundleProvider results = myPatientDao.search(map, mySrd);
List<IIdType> ids = toUnqualifiedVersionlessIds(results); List<IIdType> ids = toUnqualifiedVersionlessIds(results);
@ -2558,7 +2558,7 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test {
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true); String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
ourLog.info("Search SQL:\n{}", searchSql); ourLog.info("Search SQL:\n{}", searchSql);
assertThat(searchSql).doesNotContain("PARTITION_ID"); assertThat(searchSql).doesNotContain("PARTITION_ID");
assertThat(searchSql).containsOnlyOnce("IDX_STRING = 'Patient?birthdate=2020-01-01&family=FAM'"); assertThat(searchSql).containsOnlyOnce("IDX_STRING = 'Patient?family=FAM&gender=male'");
} }
@ -2566,13 +2566,13 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test {
public void testSearch_UniqueParam_SearchOnePartition() { public void testSearch_UniqueParam_SearchOnePartition() {
createUniqueComboSp(); createUniqueComboSp();
IIdType id = createPatient(withPartition(1), withBirthdate("2020-01-01"), withFamily("FAM")); IIdType id = createPatient(withPartition(1), withGender("male"), withFamily("FAM"));
addReadPartition(1); addReadPartition(1);
myCaptureQueriesListener.clear(); myCaptureQueriesListener.clear();
SearchParameterMap map = new SearchParameterMap(); SearchParameterMap map = new SearchParameterMap();
map.add(Patient.SP_FAMILY, new StringParam("FAM")); map.add(Patient.SP_FAMILY, new StringParam("FAM"));
map.add(Patient.SP_BIRTHDATE, new DateParam("2020-01-01")); map.add(Patient.SP_GENDER, new TokenParam(null, "male"));
map.setLoadSynchronous(true); map.setLoadSynchronous(true);
IBundleProvider results = myPatientDao.search(map, mySrd); IBundleProvider results = myPatientDao.search(map, mySrd);
List<IIdType> ids = toUnqualifiedVersionlessIds(results); List<IIdType> ids = toUnqualifiedVersionlessIds(results);
@ -2582,13 +2582,13 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test {
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true); String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
ourLog.info("Search SQL:\n{}", searchSql); ourLog.info("Search SQL:\n{}", searchSql);
assertThat(searchSql).containsOnlyOnce( "PARTITION_ID = '1'"); assertThat(searchSql).containsOnlyOnce( "PARTITION_ID = '1'");
assertThat(searchSql).containsOnlyOnce("IDX_STRING = 'Patient?birthdate=2020-01-01&family=FAM'"); assertThat(searchSql).containsOnlyOnce("IDX_STRING = 'Patient?family=FAM&gender=male'");
// Same query, different partition // Same query, different partition
addReadPartition(2); addReadPartition(2);
myCaptureQueriesListener.clear(); myCaptureQueriesListener.clear();
map = new SearchParameterMap(); map = new SearchParameterMap();
map.add(Patient.SP_BIRTHDATE, new DateParam("2020-01-01")); map.add(Patient.SP_GENDER, new TokenParam(null, "male"));
map.setLoadSynchronous(true); map.setLoadSynchronous(true);
results = myPatientDao.search(map, mySrd); results = myPatientDao.search(map, mySrd);
ids = toUnqualifiedVersionlessIds(results); ids = toUnqualifiedVersionlessIds(results);
@ -2661,7 +2661,7 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test {
public void testSearch_RefParam_TargetPid_SearchOnePartition() { public void testSearch_RefParam_TargetPid_SearchOnePartition() {
createUniqueComboSp(); createUniqueComboSp();
IIdType patientId = createPatient(withPartition(myPartitionId), withBirthdate("2020-01-01")); IIdType patientId = createPatient(withPartition(myPartitionId), withGender("male"));
IIdType observationId = createObservation(withPartition(myPartitionId), withSubject(patientId)); IIdType observationId = createObservation(withPartition(myPartitionId), withSubject(patientId));
addReadPartition(myPartitionId); addReadPartition(myPartitionId);
@ -2698,7 +2698,7 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test {
public void testSearch_RefParam_TargetPid_SearchDefaultPartition() { public void testSearch_RefParam_TargetPid_SearchDefaultPartition() {
createUniqueComboSp(); createUniqueComboSp();
IIdType patientId = createPatient(withPartition(null), withBirthdate("2020-01-01")); IIdType patientId = createPatient(withPartition(null), withGender("male"));
IIdType observationId = createObservation(withPartition(null), withSubject(patientId)); IIdType observationId = createObservation(withPartition(null), withSubject(patientId));
addReadDefaultPartition(); addReadDefaultPartition();
@ -2735,7 +2735,7 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test {
public void testSearch_RefParam_TargetForcedId_SearchOnePartition() { public void testSearch_RefParam_TargetForcedId_SearchOnePartition() {
createUniqueComboSp(); createUniqueComboSp();
IIdType patientId = createPatient(withPartition(myPartitionId), withId("ONE"), withBirthdate("2020-01-01")); IIdType patientId = createPatient(withPartition(myPartitionId), withId("ONE"), withGender("male"));
IIdType observationId = createObservation(withPartition(myPartitionId), withSubject(patientId)); IIdType observationId = createObservation(withPartition(myPartitionId), withSubject(patientId));
addReadPartition(myPartitionId); addReadPartition(myPartitionId);
@ -2805,7 +2805,7 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test {
public void testSearch_RefParam_TargetForcedId_SearchDefaultPartition() { public void testSearch_RefParam_TargetForcedId_SearchDefaultPartition() {
createUniqueComboSp(); createUniqueComboSp();
IIdType patientId = createPatient(withPartition(null), withId("ONE"), withBirthdate("2020-01-01")); IIdType patientId = createPatient(withPartition(null), withId("ONE"), withGender("male"));
IIdType observationId = createObservation(withPartition(null), withSubject(patientId)); IIdType observationId = createObservation(withPartition(null), withSubject(patientId));
addReadDefaultPartition(); addReadDefaultPartition();

View File

@ -15,6 +15,11 @@ import org.w3c.dom.Document;
import org.w3c.dom.Element; import org.w3c.dom.Element;
import org.w3c.dom.NodeList; import org.w3c.dom.NodeList;
import org.xml.sax.SAXException; import org.xml.sax.SAXException;
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import org.hl7.fhir.r5.model.Composition;
import org.junit.jupiter.api.Test;
import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerException;
import java.io.IOException; import java.io.IOException;
@ -105,6 +110,20 @@ public class FhirPatchCoreTest extends BaseTest {
return retVal; return retVal;
} }
@Test
void doDiffOnComposition() throws FileNotFoundException {
FhirPatch differ = new FhirPatch(FhirContext.forR5());
var original = FhirContext.forR5().newJsonParser().parseResource(Composition.class, new DataInputStream(new FileInputStream("src/test/resources/origin.json")));
var focused = FhirContext.forR5().newJsonParser().parseResource(Composition.class, new DataInputStream(new FileInputStream("src/test/resources/focused.json")));
var diff = differ.diff(original, focused);
FhirContext.forR5().newJsonParser().encodeResourceToString(diff);
}
private static Element getFirstChildElement(Element theInput) { private static Element getFirstChildElement(Element theInput) {
for (int i = 0; i < theInput.getChildNodes().getLength(); i++) { for (int i = 0; i < theInput.getChildNodes().getLength(); i++) {
if (theInput.getChildNodes().item(i) instanceof Element) { if (theInput.getChildNodes().item(i) instanceof Element) {

View File

@ -0,0 +1,366 @@
{
"resourceType": "Composition",
"id": "Processedcompositionf4d45353edcb21af3718d3a0df94a4d8",
"meta": {
"profile": [
"http://hl7.org/fhir/uv/emedicinal-product-info/StructureDefinition/Composition-uv-epi"
]
},
"extension": [
{
"url": "http://hl7.eu/fhir/ig/gravitate-health/StructureDefinition/HtmlElementLink",
"extension": [
{
"url": "elementClass",
"valueString": "pregnancyCategory"
},
{
"url": "concept",
"valueCodeableReference": {
"concept": {
"coding": [
{
"system": "http://snomed.info/sct",
"code": "77386006",
"display": "Pregnancy"
}
]
}
}
}
]
},
{
"url": "http://hl7.eu/fhir/ig/gravitate-health/StructureDefinition/HtmlElementLink",
"extension": [
{
"url": "elementClass",
"valueString": "breastfeedingCategory"
},
{
"url": "concept",
"valueCodeableReference": {
"concept": {
"coding": [
{
"system": "http://snomed.info/sct",
"code": "69840006",
"display": "Normal breast feeding (finding)"
}
]
}
}
}
]
},
{
"url": "http://hl7.eu/fhir/ig/gravitate-health/StructureDefinition/HtmlElementLink",
"extension": [
{
"url": "elementClass",
"valueString": "indication"
},
{
"url": "concept",
"valueCodeableReference": {
"reference": {
"reference": "ClinicalUseDefinition/cud-585e364c14debe29f6c6b564138aa400"
}
}
}
]
},
{
"url": "http://hl7.eu/fhir/ig/gravitate-health/StructureDefinition/HtmlElementLink",
"extension": [
{
"url": "elementClass",
"valueString": "contraindication"
},
{
"url": "concept",
"valueCodeableReference": {
"concept": {
"coding": [
{
"system": "http://snomed.info/sct",
"code": "410536001",
"display": "Contraindicated (qualifier value)"
}
]
}
}
}
]
},
{
"url": "http://hl7.eu/fhir/ig/gravitate-health/StructureDefinition/HtmlElementLink",
"extension": [
{
"url": "elementClass",
"valueString": "contra-indication-pregancy"
},
{
"url": "concept",
"valueCodeableReference": {
"reference": {
"reference": "ClinicalUseDefinition/contraindication-pregancy"
}
}
}
]
},
{
"url": "http://hl7.eu/fhir/ig/gravitate-health/StructureDefinition/HtmlElementLink",
"extension": [
{
"url": "elementClass",
"valueString": "contra-indication-kidney"
},
{
"url": "concept",
"valueCodeableReference": {
"reference": {
"reference": "ClinicalUseDefinition/contraindication-kidney"
}
}
}
]
},
{
"url": "http://hl7.eu/fhir/ig/gravitate-health/StructureDefinition/HtmlElementLink",
"extension": [
{
"url": "elementClass",
"valueString": "contra-indication-diabetes-mellitus"
},
{
"url": "concept",
"valueCodeableReference": {
"reference": {
"reference": "ClinicalUseDefinition/contraindication-diabetes-mellitus"
}
}
}
]
},
{
"url": "http://hl7.eu/fhir/ig/gravitate-health/StructureDefinition/HtmlElementLink",
"extension": [
{
"url": "elementClass",
"valueString": "lactose"
},
{
"url": "concept",
"valueCodeableReference": {
"concept": {
"coding": [
{
"system": "http://snomed.info/sct",
"code": "190751001",
"display": "Primary lactose intolerance"
}
]
}
}
}
]
}
],
"identifier": [
{
"system": "https://spor.ema.europa.eu/rmswi/",
"value": "0d69fdcb-33cf-407f-8209-a6529856ab4f"
}
],
"status": "final",
"type": {
"coding": [
{
"system": "https://spor.ema.europa.eu/rmswi/",
"code": "100000155538"
}
],
"text": "Package Leaflet"
},
"category": [
{
"coding": [
{
"system": "http://hl7.eu/fhir/ig/gravitate-health/CodeSystem/epicategory-cs",
"code": "P",
"display": "Processed"
}
]
}
],
"subject": [
{
"reference": "MedicinalProductDefinition/mp2412867d9a0e15f82f11047ad93bdbad"
}
],
"date": "2022-02-16T13:28:17Z",
"author": [
{
"reference": "Organization/mah-511671db37e83e520b00f8a0d817dc96"
}
],
"title": "TEST PURPOSES ONLY - Karvea",
"section": [
{
"title": "B. Package Leaflet",
"code": {
"coding": [
{
"system": "https://spor.ema.europa.eu/rmswi/",
"code": "100000155538"
}
],
"text": "B. PACKAGE LEAFLET"
},
"text": {
"status": "additional",
"div": "<div xmlns=\"http://www.w3.org/1999/xhtml\">unavailable</div>"
},
"emptyReason": {
"coding": [
{
"system": "http://terminology.hl7.org/CodeSystem/list-empty-reason",
"code": "unavailable"
}
]
},
"section": [
{
"title": "Package leaflet: Information for the user",
"code": {
"coding": [
{
"system": "https://spor.ema.europa.eu/rmswi/",
"code": "100000155538"
}
],
"text": "Package leaflet: Information for the user"
},
"text": {
"status": "additional",
"div": "<div xmlns=\"http://www.w3.org/1999/xhtml\"><div><p>Karvea 75 mg tablets</p><p>irbesartan</p><b>Read all of this leaflet carefully before you start taking this medicine because it contains important information for you.</b><ul><li>Keep this leaflet. You may need to read it again.</li><li>If you have any further questions, ask your doctor or pharmacist.</li><li>This medicine has been prescribed for you only. Do not pass it on to others. It may harm them, even if their signs of illness are the same as yours.</li><li>If you get any side effects, talk to your doctor or pharmacist. This includes any possible side effects not listed in this leaflet. See section 4.</li></ul></div></div>"
}
},
{
"title": "What is in this leaflet",
"code": {
"coding": [
{
"system": "https://spor.ema.europa.eu/rmswi/",
"code": "100000155538"
}
],
"text": "What is in this leaflet"
},
"text": {
"status": "additional",
"div": "<div xmlns=\"http://www.w3.org/1999/xhtml\"><div><ul><li>What Karvea is and what it is used for</li><li>What you need to know before you take Karvea</li><li>How to take Karvea</li><li>Possible side effects</li><li>How to store Karvea</li><li>Contents of the pack and other information</li></ul></div></div>"
}
},
{
"title": "1. What Karvea is and what it is used for",
"code": {
"coding": [
{
"system": "https://spor.ema.europa.eu/rmswi/",
"code": "100000155538"
}
],
"text": "1. What Karvea is and what it is used for"
},
"text": {
"status": "additional",
"div": "<div xmlns=\"http://www.w3.org/1999/xhtml\"><div><p>Karvea belongs to a group of medicines known as angiotensin-II receptor antagonists. Angiotensin-II is a substance produced in the body which binds to receptors in blood vessels causing them to tighten. This results in an increase in blood pressure. Karvea prevents the binding of angiotensin-II to these receptors, causing the blood vessels to relax and the blood pressure to lower. Karvea slows the decrease of kidney function in patients with high blood pressure and type 2 diabetes.</p><span class=\"indication\"></span><p>Karvea is used in adult patients</p><ul><li>to treat high blood pressure (essential hypertension)</li><li>to protect the kidney in patients with high blood pressure, type 2 diabetes and laboratory evidence of impaired kidney function.</li></ul></div></div>"
}
},
{
"title": "2. What you need to know before you take Karvea",
"code": {
"coding": [
{
"system": "https://spor.ema.europa.eu/rmswi/",
"code": "100000155538"
}
],
"text": "2. What you need to know before you take Karvea"
},
"text": {
"status": "additional",
"div": "<div xmlns=\"http://www.w3.org/1999/xhtml\"><div><span class=\"contraindication\"><b>Do not take Karvea</b></span><ul><li>if you are allergic to irbesartan or any other ingredients of this medicine (listed in section 6)</li><span class=\"contra-indication-pregancy\"></span><li>if you are more than 3 months pregnant. (It is also better to avoid Karvea in early pregnancy see pregnancy section)</li><li><b><span class=\"contra-indication-diabetes-mellitus highlight\">if you have diabetes </span>or <span class=\"contra-indication-kidney\">impaired kidney function</span></b> and you are treated with a blood pressure lowering medicine containing aliskiren.</li></ul><b>Warning and precautions</b><p>Talk to your doctor before taking Karvea and <b>if any of the following apply to you:</b></p><ul><li>if you get excessive vomiting or diarrhoea</li><li>if you suffer from kidney problems</li><li>if you suffer from heart problems</li><li>if you receive Karvea for diabetic kidney disease. In this case your doctor may perform regular blood tests, especially for measuring blood potassium levels in case of poor kidney function</li><li>if you develop low blood sugar levels (symptoms may include sweating, weakness, hunger, dizziness, trembling, headache, flushing or paleness, numbness, having a fast, pounding heart beat), particularly if you are being treated for diabetes.</li><li>if you are going to have an operation (surgery) or be given anaesthetics</li><li><ul><li>an ACE-inhibitor (for example enalapril, lisinopril, ramipril), in particular if you have diabetes-related kidney problems.</li><li>aliskiren</li></ul></li></ul><p>Your doctor may check your kidney function, blood pressure, and the amount of electrolytes (e.g. potassium) in your blood at regular intervals.</p><p>See also information under the heading “Do not take Karvea”.</p><p>You must tell your doctor if you think you are (or might become) pregnant. Karvea is not recommended in early pregnancy, and must not be taken if you are more than 3 months pregnant, as it may cause serious harm to your baby if used at that stage (see pregnancy section).</p><b>Children and adolescents</b><p>This medicinal product should not be used in children and adolescents because the safety and efficacy have not yet been fully established.</p><b>Other medicines and Karvea</b><p>Tell your doctor or pharmacist if you are taking, have recently taken or might take any other medicines.</p><p>Your doctor may need to change your dose and/or to take other precautions: If you are taking an ACE-inhibitor or aliskiren (see also information under the headings “Do not take Karvea” and “Warnings and precautions”).</p><b>You may need to have blood checks if you take:</b><ul><li>potassium supplements</li><li>salt substitutes containing potassium</li><li>potassium-sparing medicines (such as certain diuretics)</li><li>medicines containing lithium</li><li>repaglinide (medication used for lowering blood sugar levels)</li></ul><p>If you take certain painkillers, called non-steroidal anti-inflammatory drugs, the effect of irbesartan may be reduced.</p><b>Karvea with food and drink</b><p>Karvea can be taken with or without food.</p><b>Pregnancy and breast-feeding</b> <span class=\"pregnancyCategory highlight\"><b>Pregnancy</b></span><p>You must tell your doctor if you think you are (or might become) pregnant. Your doctor will normally advise you to stop taking Karvea before you become pregnant or as soon as you know you are pregnant and will advise you to take another medicine instead of Karvea. Karvea is not recommended in early pregnancy, and must not be taken when more than 3 months pregnant, as it may cause serious harm to your baby if used after the third month of pregnancy.</p><span class=\"breastfeedingCategory highlight\"><b>Breast-feeding</b></span><p>Tell your doctor if you are breast-feeding or about to start breast-feeding. Karvea is not recommended for mothers who are breast-feeding, and your doctor may choose another treatment for you if you wish to breast-feed, especially if your baby is newborn, or was born prematurely.</p><b>Driving and using machines</b><p>Karvea is unlikely to affect your ability to drive or use machines. However, occasionally dizziness or weariness may occur during treatment of high blood pressure. If you experience these, talk to your doctor before attempting to drive or use machines.</p><span class=\"lactose\"><b>Karvea contains lactose.</b></span><p>If you have been told by your doctor that you have an intolerance to some sugars (e.g. lactose), contact your doctor before taking this medicinal product.</p><b>Karvea contains sodium.</b><p>This medicine contains less than 1 mmol sodium (23 mg) per tablet, that is to say essentially sodium-free.</p></div></div>"
}
},
{
"title": "3. How to take Karvea",
"code": {
"coding": [
{
"system": "https://spor.ema.europa.eu/rmswi/",
"code": "100000155538"
}
],
"text": "3. How to take Karvea"
},
"text": {
"status": "additional",
"div": "<div xmlns=\"http://www.w3.org/1999/xhtml\"><div><b>Always take this medicine exactly as your doctor has told you. Check with your doctor or pharmacist if you are not sure.</b> <b>Method of administration</b><p>Karvea is for oral use. Swallow the tablets with a sufficient amount of fluid (e.g. one glass of water). You can take Karvea with or without food. Try to take your daily dose at about the same time each day. It is important that you continue to take Karvea until your doctor tells you otherwise.</p><ul><li><b>Patients with high blood pressure</b><p>The usual dose is 150 mg once a day (two tablets a day). The dose may later be increased to 300 mg (four tablets a day) once daily depending on blood pressure response.</p></li><li><b>Patients with high blood pressure and type 2 diabetes with kidney disease</b><p>In patients with high blood pressure and type 2 diabetes, 300 mg (four tablets a day) once daily is the preferred maintenance dose for the treatment of associated kidney disease.</p></li></ul><p>The doctor may advise a lower dose, especially when starting treatment in certain patients such as those on haemodialysis, or those over the age of 75 years.</p><p>The maximal blood pressure lowering effect should be reached 4-6 weeks after beginning treatment.</p><b>Use in children and adolescents</b><p>Karvea should not be given to children under 18 years of age. If a child swallows some tablets, contact your doctor immediately.</p><b>If you take more Karvea than you should</b><p>If you accidentally take too many tablets, contact your doctor immediately.</p><b>If you forget to take Karvea</b><p>If you accidentally miss a daily dose, just take the next dose as normal. Do not take a double dose to make up for a forgotten dose.</p><p>If you have any further questions on the use of this medicine, ask your doctor or pharmacist.</p></div></div>"
}
},
{
"title": "4. Possible side effects",
"code": {
"coding": [
{
"system": "https://spor.ema.europa.eu/rmswi/",
"code": "100000155538"
}
],
"text": "4. Possible side effects"
},
"text": {
"status": "additional",
"div": "<div xmlns=\"http://www.w3.org/1999/xhtml\"><div><p>Like all medicines, this medicine can cause side effects, although not everybody gets them. Some of these effects may be serious and may require medical attention.</p><p>As with similar medicines, rare cases of allergic skin reactions (rash, urticaria), as well as localised swelling of the face, lips and/or tongue have been reported in patients taking irbesartan. If you get any of these symptoms or get short of breath, stop taking Karvea and contact your doctor immediately.</p><p>The frequency of the side effects listed below is defined using the following convention:</p><p>Very common: may affect more than 1 in 10 people</p><p>Common: may affect up to 1 in 10 people</p><p>Uncommon: may affect up to 1 in 100 people</p><p>Side effects reported in clinical studies for patients treated with Karvea were:</p><ul><li>Very common (may affect more than 1 in 10 people): if you suffer from high blood pressure and type 2 diabetes with kidney disease, blood tests may show an increased level of potassium.</li><li>Common (may affect up to 1 10 people): dizziness, feeling sick/vomiting, fatigue and blood tests may show raised levels of an enzyme that measures the muscle and heart function (creatine kinase enzyme). In patients with high blood pressure and type 2 diabetes with kidney disease, dizziness when getting up from a lying or sitting position, low blood pressure when getting up from a lying or sitting position, pain in joints or muscles and decreased levels of a protein in the red blood cells (haemoglobin) were also reported.</li><li>Uncommon (may affect up to 1 in 100 people): heart rate increased, flushing, cough, diarrhoea, indigestion/heartburn, sexual dysfunction (problems with sexual performance), chest pain.</li></ul><p>Some undesirable effects have been reported since marketing of Karvea. Undesirable effects where the frequency is not known are: feeling of spinning, headache, taste disturbance, ringing in the ears, muscle cramps, pain in joints and muscles, decreased number of red blood cells (anaemia symptoms may include tiredness, headaches, being short of breath when exercising, dizziness and looking pale), reduced number of platelets, abnormal liver function, increased blood potassium levels, impaired kidney function, inflammation of small blood vessels mainly affecting the skin (a condition known as leukocytoclastic vasculitis), severe allergic reactions (anaphylactic shock) and low blood sugar levels. Uncommon cases of jaundice (yellowing of the skin and/or whites of the eyes) have also been reported.</p><b>Reporting of side effects</b><p>If you get any side effects, talk to your doctor or pharmacist. This includes any possible side effects not listed in this leaflet. You can also report side effects directly via the national reporting system listed in Appendix V. By reporting side effects you can help provide more information on the safety of this medicine.</p></div></div>"
}
},
{
"title": "5. How to store Karvea",
"code": {
"coding": [
{
"system": "https://spor.ema.europa.eu/rmswi/",
"code": "100000155538"
}
],
"text": "5. How to store Karvea"
},
"text": {
"status": "additional",
"div": "<div xmlns=\"http://www.w3.org/1999/xhtml\"><div><p>Keep this medicine out of the sight and reach of children.</p><p>Do not use this medicine after the expiry date which is stated on the carton and on the blister after EXP. The expiry date refers to the last day of that month.</p><p>Do not store above 30°C.</p><p>Do not throw away any medicines via wastewater or household waste. Ask your pharmacist how to throw away of medicines you no longer use. These measures will help protect the environment.</p></div></div>"
}
},
{
"title": "6. Contents of the pack and other information",
"code": {
"coding": [
{
"system": "https://spor.ema.europa.eu/rmswi/",
"code": "100000155538"
}
],
"text": "6. Contents of the pack and other information"
},
"text": {
"status": "additional",
"div": "<div xmlns=\"http://www.w3.org/1999/xhtml\"><div><b>What Karvea contains</b><ul><li>The active substance is irbesartan. Each tablet of Karvea 75 mg contains 75 mg irbesartan.</li><li>The other ingredients are microcrystalline cellulose, croscarmellose sodium, lactose monohydrate, magnesium stearate, colloidal hydrated silica, pregelatinised maize starch, and poloxamer 188. Please see section 2 “Karvea contains lactose”.</li></ul><b>What Karvea looks like and contents of the pack</b><p>Karvea 75 mg tablets are white to off-white, biconvex, and oval-shaped with a heart debossed on one side and the number 2771 engraved on the other side.</p><p>Karvea 75 mg tablets are supplied in blister packs of 14, 28, 56 or 98 tablets. Unidose blister packs of 56 x 1 tablet for delivery in hospitals are also available.</p><p>Not all pack sizes may be marketed.</p><b>Marketing Authorisation Holder:</b><p>sanofi-aventis groupe</p><p>54, rue La Boétie</p><p>F-75008 Paris - France</p><p>Manufacturer:</p><p>SANOFI WINTHROP INDUSTRIE</p><p>1, rue de la Vierge</p><p>Ambarès and Lagrave</p><p>F-33565 Carbon Blanc Cedex - France</p><p>SANOFI WINTHROP INDUSTRIE</p><p>30-36 Avenue Gustave Eiffel, BP 7166</p><p>F-37071 Tours Cedex 2 - France</p><p>For any information about this medicinal product, please contact the local representative of the Marketing Authorisation Holder.</p><table><colgroup><col/><col/></colgroup><thead><tr><th></th><th></th></tr></thead><tbody><tr><td><p><b>België/Belgique/Belgien</b></p><p>Sanofi Belgium</p><p>Tél/Tel: +32 (0)2 710 54 00</p></td><td><p><b>Lietuva</b></p><p>Swixx Biopharma UAB</p><p>Tel: +370 5 236 91 40</p></td></tr><tr><td><p><b>България</b></p><p>Swixx Biopharma EOOD Тел.: +359 (0)2 4942 480</p></td><td><p><b>Luxembourg/Luxemburg</b></p><p>Sanofi Belgium</p><p>Tél/Tel: +32 (0)2 710 54 00 (Belgique/Belgien)</p></td></tr><tr><td><p><b>Česká republika</b></p><p>sanofi-aventis, s.r.o. Tel: +420 233 086 111</p></td><td><p><b>Magyarország</b></p><p>SANOFI-A VENTIS Zrt. Tel.: +36 1 505 0050</p></td></tr><tr><td><p><b>Danmark</b></p><p>Sanofi A/S</p><p>Tlf: +45 45 16 70 00</p></td><td><p><b>Malta</b></p><p>Sanofi S.r.l.</p><p>Tel: +39 02 39394275</p></td></tr><tr><td><p><b>Deutschland</b></p><p>Sanofi-Aventis Deutschland GmbH</p><p>Tel: 0800 52 52 010</p><p>Tel. aus dem Ausland: +49 69 305 21 131</p></td><td><p><b>Norge</b></p><p>sanofi-aventis Norge AS Tlf: +47 67 10 71 00</p></td></tr><tr><td><p><b>Eesti</b></p><p>Swixx Biopharma OÜ Tel: +372 640 10 30</p></td><td><p><b>Österreich</b></p><p>sanofi-aventis GmbH Tel: +43 1 80 185 0</p></td></tr><tr><td><p><b>Ελλάδα</b></p><p>sanofi-aventis AEBE Τηλ: +30 210 900 16 00</p></td><td><p><b>Polska</b></p><p>sanofi-aventis Sp. z o.o. Tel.: +48 22 280 00 00</p></td></tr><tr><td><p><b>España</b></p><p>sanofi-aventis, S.A. Tel: +34 93 485 94 00</p></td><td></td></tr><tr><td><p><b>France</b></p><p>sanofi-aventis France</p><p>Tél: 0 800 222 555</p><p>Appel depuis létranger : +33 1 57 63 23 23</p></td><td><p><b>Portugal</b></p><p>Sanofi - Produtos Farmacêuticos, Lda Tel: +351 21 35 89 400</p></td></tr><tr><td><p><b>Hrvatska</b></p><p>Swixx Biopharma d.o.o. Tel: +385 1 2078 500</p></td><td><p><b>România</b></p><p>Sanofi Romania SRL Tel: +40 (0) 21 317 31 36</p></td></tr><tr><td><p><b>Ireland</b></p><p>sanofi-aventis Ireland Ltd. T/A SANOFI Tel: +353 (0) 1 403 56 00</p></td><td><p><b>Slovenija</b></p><p>Swixx Biopharma d.o.o. Tel: +386 1 235 51 00</p></td></tr><tr><td><p><b>Ísland</b></p><p>Vistor hf.</p><p>Sími: +354 535 7000</p></td><td><p><b>Slovenská republika</b></p><p>Swixx Biopharma s.r.o. Tel: +421 2 208 33 600</p></td></tr><tr><td><p><b>Italia</b></p><p>Sanofi S.r.l. Tel: 800 536389</p></td><td><p><b>Suomi/Finland</b></p><p>Sanofi Oy</p><p>Puh/Tel: +358 (0) 201 200 300</p></td></tr><tr><td><p><b>Κύπρος</b></p><p>C.A. Papaellinas Ltd. Τηλ: +357 22 741741</p></td><td><p><b>Sverige</b></p><p>Sanofi AB</p><p>Tel: +46 (0)8 634 50 00</p></td></tr><tr><td><p><b>Latvija</b></p><p>Swixx Biopharma SIA Tel: +371 6 616 47 50</p></td><td><p><b>United Kingdom (Northern Ireland)</b></p><p>sanofi-aventis Ireland Ltd. T/A SANOFI Tel: +44 (0) 800 035 2525</p></td></tr><tr><td></td><td></td></tr><tr><td></td><td></td></tr><tr><td></td><td></td></tr><tr><td></td><td></td></tr><tr><td></td><td></td></tr><tr><td></td><td></td></tr></tbody></table><p>This leaflet was last revised in</p><p>Detailed information on this medicine is available on the European Medicines Agency web site: http://www.ema.europa.eu/</p></div></div>"
}
}
]
}
]
}

File diff suppressed because one or more lines are too long