More terminology service work

This commit is contained in:
jamesagnew 2016-06-03 08:16:40 -04:00
parent c318d1a040
commit 32cebb2a9f
13 changed files with 456 additions and 172 deletions

View File

@ -0,0 +1,168 @@
package example;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import java.io.File;
import java.io.FileReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.io.IOUtils;
import org.hl7.fhir.dstu3.hapi.validation.DefaultProfileValidationSupport;
import org.hl7.fhir.dstu3.hapi.validation.FhirInstanceValidator;
import org.hl7.fhir.dstu3.hapi.validation.IValidationSupport;
import org.hl7.fhir.dstu3.hapi.validation.ValidationSupportChain;
import org.hl7.fhir.dstu3.model.CodeSystem;
import org.hl7.fhir.dstu3.model.StructureDefinition;
import org.hl7.fhir.dstu3.model.ValueSet;
import org.hl7.fhir.dstu3.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.dstu3.model.ValueSet.ValueSetExpansionComponent;
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
import org.hl7.fhir.instance.model.api.IBaseResource;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.validation.FhirValidator;
import ca.uhn.fhir.validation.ValidationResult;
public class ValidateDirectory {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ValidateDirectory.class);
public static void main(String[] args) throws Exception {
// Load all profiles in this directory
File profileDirectory = new File("/tmp/directory/with/profiles");
// Validate resources in this directory
File resourceDirectory = new File("/tmp/directory/with/resources/to/validate");
FhirContext ctx = FhirContext.forDstu3();
IParser xmlParser = ctx.newXmlParser();
IParser jsonParser = ctx.newJsonParser();
Map<String, StructureDefinition> structureDefinitions = new HashMap<String, StructureDefinition>();
Map<String, CodeSystem> codeSystems = new HashMap<String, CodeSystem>();
Map<String, ValueSet> valueSets = new HashMap<String, ValueSet>();
// Load all profile files
for (File nextFile : profileDirectory.listFiles()) {
IBaseResource parsedRes = null;
if (nextFile.getAbsolutePath().toLowerCase().endsWith(".xml")) {
parsedRes = xmlParser.parseResource(new FileReader(nextFile));
} else if (nextFile.getAbsolutePath().toLowerCase().endsWith(".json")) {
parsedRes = jsonParser.parseResource(new FileReader(nextFile));
} else {
ourLog.info("Ignoring file: {}", nextFile.getName());
}
if (parsedRes instanceof StructureDefinition) {
StructureDefinition res = (StructureDefinition) parsedRes;
if (isNotBlank(res.getUrl())) {
structureDefinitions.put(res.getUrl(), res);
}
} else if (parsedRes instanceof ValueSet) {
ValueSet res = (ValueSet) parsedRes;
if (isNotBlank(res.getUrl())) {
valueSets.put(res.getUrl(), res);
}
} else if (parsedRes instanceof CodeSystem) {
CodeSystem res = (CodeSystem) parsedRes;
if (isNotBlank(res.getUrl())) {
codeSystems.put(res.getUrl(), res);
}
}
}
FhirInstanceValidator instanceValidator = new FhirInstanceValidator();
ValidationSupportChain validationSupportChain = new ValidationSupportChain();
validationSupportChain.addValidationSupport(new DefaultProfileValidationSupport());
validationSupportChain.addValidationSupport(new PrePopulatedValidationSupport(structureDefinitions, valueSets, codeSystems));
instanceValidator.setValidationSupport(validationSupportChain);
FhirValidator val = ctx.newValidator();
val.registerValidatorModule(instanceValidator);
// Loop through the files in the validation directory and validate each one
for (File nextFile : resourceDirectory.listFiles()) {
if (nextFile.getAbsolutePath().toLowerCase().endsWith(".xml")) {
ourLog.info("Going to validate: {}", nextFile.getName());
} else if (nextFile.getAbsolutePath().toLowerCase().endsWith(".json")) {
ourLog.info("Going to validate: {}", nextFile.getName());
} else {
ourLog.info("Ignoring file: {}", nextFile.getName());
continue;
}
String input = IOUtils.toString(new FileReader(nextFile));
ValidationResult result = val.validateWithResult(input);
IBaseOperationOutcome oo = result.toOperationOutcome();
ourLog.info("Result:\n{}", xmlParser.setPrettyPrint(true).encodeResourceToString(oo));
}
}
public static class PrePopulatedValidationSupport implements IValidationSupport {
private Map<String, StructureDefinition> myStructureDefinitions;
private Map<String, ValueSet> myValueSets;
private Map<String, CodeSystem> myCodeSystems;
public PrePopulatedValidationSupport(Map<String, StructureDefinition> theStructureDefinitions, Map<String, ValueSet> theValueSets, Map<String, CodeSystem> theCodeSystems) {
myStructureDefinitions = theStructureDefinitions;
myValueSets = theValueSets;
myCodeSystems = theCodeSystems;
}
@Override
public ValueSetExpansionComponent expandValueSet(FhirContext theContext, ConceptSetComponent theInclude) {
return null;
}
@Override
public List<StructureDefinition> fetchAllStructureDefinitions(FhirContext theContext) {
return new ArrayList<StructureDefinition>(myStructureDefinitions.values());
}
@Override
public CodeSystem fetchCodeSystem(FhirContext theContext, String theSystem) {
return myCodeSystems.get(theSystem);
}
@SuppressWarnings("unchecked")
@Override
public <T extends IBaseResource> T fetchResource(FhirContext theContext, Class<T> theClass, String theUri) {
if (theClass.equals(StructureDefinition.class)) {
return (T) myStructureDefinitions.get(theUri);
}
if (theClass.equals(ValueSet.class)) {
return (T) myValueSets.get(theUri);
}
if (theClass.equals(CodeSystem.class)) {
return (T) myCodeSystems.get(theUri);
}
return null;
}
@Override
public StructureDefinition fetchStructureDefinition(FhirContext theCtx, String theUrl) {
return myStructureDefinitions.get(theUrl);
}
@Override
public boolean isCodeSystemSupported(FhirContext theContext, String theSystem) {
return false;
}
@Override
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) {
return null;
}
}
}

View File

@ -30,6 +30,7 @@ import java.util.List;
import java.util.Set;
import org.apache.commons.codec.binary.StringUtils;
import org.hl7.fhir.dstu3.exceptions.TerminologyServiceException;
import org.hl7.fhir.dstu3.hapi.validation.HapiWorkerContext;
import org.hl7.fhir.dstu3.hapi.validation.IValidationSupport;
import org.hl7.fhir.dstu3.model.CodeSystem;
@ -75,7 +76,7 @@ public class FhirResourceDaoValueSetDstu3 extends FhirResourceDaoDstu3<ValueSet>
String filterLc = theFilter != null ? theFilter.toLowerCase() : null;
ValueSetExpansionOutcome outcome = workerContext.expand(source);
ValueSetExpansionComponent expansion = outcome.getValueset().getExpansion();
ValueSetExpansionComponent expansion = outcome.getValueset().getExpansion();
if (isNotBlank(theFilter)) {
for (Iterator<ValueSetExpansionContainsComponent> containsIter = expansion.getContains().iterator(); containsIter.hasNext();) {
ValueSetExpansionContainsComponent nextContains = containsIter.next();

View File

@ -207,11 +207,12 @@ public class TermConcept implements Serializable {
}
}
public void setDisplay(String theDisplay) {
public TermConcept setDisplay(String theDisplay) {
myDisplay = theDisplay;
if (isNotBlank(theDisplay) && theDisplay.length() > MAX_DESC_LENGTH) {
myDisplay = myDisplay.substring(0, MAX_DESC_LENGTH);
}
return this;
}
public void setParentPids(Set<Long> theParentPids) {

View File

@ -66,27 +66,18 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc {
@Autowired
protected ITermConceptDao myConceptDao;
@Autowired
private DaoConfig myDaoConfig;
@Autowired
private ITermConceptParentChildLinkDao myConceptParentChildLinkDao;
@Autowired
protected FhirContext myContext;
@Autowired
private DaoConfig myDaoConfig;
@PersistenceContext(type = PersistenceContextType.TRANSACTION)
protected EntityManager myEntityManager;
private void fetchChildren(TermConcept theConcept, Set<TermConcept> theSetToPopulate) {
for (TermConceptParentChildLink nextChildLink : theConcept.getChildren()) {
TermConcept nextChild = nextChildLink.getChild();
if (addToSet(theSetToPopulate, nextChild)) {
fetchChildren(nextChild, theSetToPopulate);
}
}
}
private boolean addToSet(Set<TermConcept> theSetToPopulate, TermConcept theConcept) {
boolean retVal = theSetToPopulate.add(theConcept);
if (retVal) {
@ -98,6 +89,15 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc {
return retVal;
}
private void fetchChildren(TermConcept theConcept, Set<TermConcept> theSetToPopulate) {
for (TermConceptParentChildLink nextChildLink : theConcept.getChildren()) {
TermConcept nextChild = nextChildLink.getChild();
if (addToSet(theSetToPopulate, nextChild)) {
fetchChildren(nextChild, theSetToPopulate);
}
}
}
private TermConcept fetchLoadedCode(Long theCodeSystemResourcePid, Long theCodeSystemVersionPid, String theCode) {
TermCodeSystemVersion codeSystem = myCodeSystemVersionDao.findByCodeSystemResourceAndVersion(theCodeSystemResourcePid, theCodeSystemVersionPid);
TermConcept concept = myConceptDao.findByCodeSystemAndCode(codeSystem, theCode);
@ -113,6 +113,17 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc {
}
}
public TermConcept findCode(String theCodeSystem, String theCode) {
TermCodeSystemVersion csv = findCurrentCodeSystemVersionForSystem(theCodeSystem);
return myConceptDao.findByCodeSystemAndCode(csv, theCode);
}
@Override
public List<TermConcept> findCodes(String theSystem) {
return myConceptDao.findByCodeSystemVersion(findCurrentCodeSystemVersionForSystem(theSystem));
}
@Transactional(propagation = Propagation.REQUIRED)
@Override
public Set<TermConcept> findCodesAbove(Long theCodeSystemResourcePid, Long theCodeSystemVersionPid, String theCode) {
@ -177,6 +188,20 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc {
return retVal;
}
private TermCodeSystemVersion findCurrentCodeSystemVersionForSystem(String theCodeSystem) {
TermCodeSystem cs = getCodeSystem(theCodeSystem);
if (cs == null || cs.getCurrentVersion() == null) {
return null;
}
TermCodeSystemVersion csv = cs.getCurrentVersion();
return csv;
}
private TermCodeSystem getCodeSystem(String theSystem) {
TermCodeSystem cs = myCodeSystemDao.findByCodeSystemUri(theSystem);
return cs;
}
private void persistChildren(TermConcept theConcept, TermCodeSystemVersion theCodeSystem, IdentityHashMap<TermConcept, Object> theConceptsStack, HashSet<Long> thePidsInHierarchy) {
if (theConceptsStack.put(theConcept, PLACEHOLDER_OBJECT) != null) {
return;
@ -277,11 +302,6 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc {
return cs != null;
}
private TermCodeSystem getCodeSystem(String theSystem) {
TermCodeSystem cs = myCodeSystemDao.findByCodeSystemUri(theSystem);
return cs;
}
private ArrayList<VersionIndependentConcept> toVersionIndependentConcepts(String theSystem, Set<TermConcept> codes) {
ArrayList<VersionIndependentConcept> retVal = new ArrayList<VersionIndependentConcept>(codes.size());
for (TermConcept next : codes) {
@ -309,14 +329,4 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc {
return retVal;
}
public TermConcept findCode(String theCodeSystem, String theCode) {
TermCodeSystem cs = getCodeSystem(theCodeSystem);
if (cs == null || cs.getCurrentVersion() == null) {
return null;
}
TermCodeSystemVersion csv = cs.getCurrentVersion();
return myConceptDao.findByCodeSystemAndCode(csv, theCode);
}
}

View File

@ -121,60 +121,82 @@ public class HapiTerminologySvcDstu3 extends BaseHapiTerminologySvc implements I
TermCodeSystem cs = myCodeSystemDao.findByCodeSystemUri(system);
TermCodeSystemVersion csv = cs.getCurrentVersion();
FullTextEntityManager em = org.hibernate.search.jpa.Search.getFullTextEntityManager(myEntityManager);
QueryBuilder qb = em.getSearchFactory().buildQueryBuilder().forEntity(TermConcept.class).get();
BooleanJunction<?> bool = qb.bool();
ValueSetExpansionComponent retVal = new ValueSetExpansionComponent();
Set<String> addedCodes = new HashSet<String>();
boolean haveIncludeCriteria = false;
bool.must(qb.keyword().onField("myCodeSystemVersionPid").matching(csv.getPid()).createQuery());
Set<String> wantCodes = new HashSet<String>();
/*
* Include Concepts
*/
for (ConceptReferenceComponent next : theInclude.getConcept()) {
String nextCode = next.getCode();
if (isNotBlank(nextCode)) {
wantCodes.add(nextCode);
bool.should(qb.keyword().onField("myCode").matching(nextCode).createQuery());
if (isNotBlank(nextCode) && !addedCodes.contains(nextCode)) {
haveIncludeCriteria = true;
TermConcept code = super.findCode(system, nextCode);
if (code != null) {
addedCodes.add(nextCode);
ValueSetExpansionContainsComponent contains = retVal.addContains();
contains.setCode(nextCode);
contains.setSystem(system);
contains.setDisplay(code.getDisplay());
}
}
}
for (ConceptSetFilterComponent nextFilter : theInclude.getFilter()) {
if (nextFilter.getProperty().equals("display") && nextFilter.getOp() == FilterOperator.EQUAL) {
if (isNotBlank(nextFilter.getValue())) {
bool.must(qb.phrase().onField("myDisplay").sentence(nextFilter.getValue()).createQuery());
/*
* Filters
*/
if (theInclude.getFilter().size() > 0) {
haveIncludeCriteria = true;
FullTextEntityManager em = org.hibernate.search.jpa.Search.getFullTextEntityManager(myEntityManager);
QueryBuilder qb = em.getSearchFactory().buildQueryBuilder().forEntity(TermConcept.class).get();
BooleanJunction<?> bool = qb.bool();
bool.must(qb.keyword().onField("myCodeSystemVersionPid").matching(csv.getPid()).createQuery());
for (ConceptSetFilterComponent nextFilter : theInclude.getFilter()) {
if (nextFilter.getProperty().equals("display") && nextFilter.getOp() == FilterOperator.EQUAL) {
if (isNotBlank(nextFilter.getValue())) {
bool.must(qb.phrase().onField("myDisplay").sentence(nextFilter.getValue()).createQuery());
}
} else if (nextFilter.getOp() == FilterOperator.ISA) {
if (isNotBlank(nextFilter.getValue())) {
TermConcept code = super.findCode(system, nextFilter.getValue());
bool.must(qb.keyword().onField("myParentPids").matching(code.getId()).createQuery());
}
} else {
throw new InvalidRequestException("Unknown filter property[" + nextFilter + "] + op[" + nextFilter.getOpElement().getValueAsString() + "]");
}
} else if (nextFilter.getOp() == FilterOperator.ISA) {
if (isNotBlank(nextFilter.getValue())) {
TermConcept code = super.findCode(system, nextFilter.getValue());
bool.must(qb.keyword().onField("myParentPids").matching(code.getId()).createQuery());
}
} else {
throw new InvalidRequestException("Unknown filter property[" + nextFilter + "] + op[" + nextFilter.getOpElement().getValueAsString() + "]");
}
Query luceneQuery = bool.createQuery();
FullTextQuery jpaQuery = em.createFullTextQuery(luceneQuery, TermConcept.class);
@SuppressWarnings("unchecked")
List<TermConcept> result = jpaQuery.getResultList();
for (TermConcept nextConcept : result) {
addCodeIfNotAlreadyAdded(system, retVal, addedCodes, nextConcept);
}
}
ValueSetExpansionComponent retVal = new ValueSetExpansionComponent();
Query luceneQuery = bool.createQuery();
FullTextQuery jpaQuery = em.createFullTextQuery(luceneQuery, TermConcept.class);
@SuppressWarnings("unchecked")
List<TermConcept> result = jpaQuery.getResultList();
for (TermConcept nextConcept : result) {
if (!wantCodes.isEmpty() && !wantCodes.contains(nextConcept.getCode())) {
continue;
if (!haveIncludeCriteria) {
List<TermConcept> allCodes = super.findCodes(system);
for (TermConcept nextConcept : allCodes) {
addCodeIfNotAlreadyAdded(system, retVal, addedCodes, nextConcept);
}
ValueSetExpansionContainsComponent contains = retVal.addContains();
contains.setCode(nextConcept.getCode());
contains.setSystem(system);
contains.setDisplay(nextConcept.getDisplay());
}
return retVal;
}
private void addCodeIfFilterMatches(ValueSetExpansionComponent retVal, TermConcept termCode, List<ConceptSetFilterComponent> theFilters, String theSystem) {
ValueSetExpansionContainsComponent contains = retVal.addContains();
contains.setSystem(theSystem);
contains.setCode(termCode.getCode());
contains.setDisplay(termCode.getDisplay());
private void addCodeIfNotAlreadyAdded(String system, ValueSetExpansionComponent retVal, Set<String> addedCodes, TermConcept nextConcept) {
if (addedCodes.add(nextConcept.getCode())) {
ValueSetExpansionContainsComponent contains = retVal.addContains();
contains.setCode(nextConcept.getCode());
contains.setSystem(system);
contains.setDisplay(nextConcept.getDisplay());
}
}
@Override
@ -185,7 +207,7 @@ public class HapiTerminologySvcDstu3 extends BaseHapiTerminologySvc implements I
@CoverageIgnore
@Override
public CodeSystem fetchCodeSystem(FhirContext theContext, String theSystem) {
throw new UnsupportedOperationException();
return null;
}
@Override

View File

@ -46,4 +46,6 @@ public interface IHapiTerminologySvc {
void storeNewCodeSystemVersion(String theSystem, TermCodeSystemVersion theCodeSystemVersion, RequestDetails theRequestDetails);
List<TermConcept> findCodes(String theSystem);
}

View File

@ -132,38 +132,7 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test {
@Test
public void testSearchCodeInExternalCodesystem() {
CodeSystem codeSystem = new CodeSystem();
codeSystem.setUrl(URL_MY_CODE_SYSTEM);
codeSystem.setContent(CodeSystemContentMode.NOTPRESENT);
IIdType id = myCodeSystemDao.create(codeSystem, new ServletRequestDetails()).getId().toUnqualified();
ResourceTable table = myResourceTableDao.findOne(id.getIdPartAsLong());
TermCodeSystemVersion cs = new TermCodeSystemVersion();
cs.setResource(table);
cs.setResourceVersionId(table.getVersion());
TermConcept parentA = new TermConcept(cs, "ParentA");
cs.getConcepts().add(parentA);
TermConcept childAA = new TermConcept(cs, "childAA");
parentA.addChild(childAA, RelationshipTypeEnum.ISA);
TermConcept childAAA = new TermConcept(cs, "childAAA");
childAA.addChild(childAAA, RelationshipTypeEnum.ISA);
TermConcept childAAB = new TermConcept(cs, "childAAB");
childAA.addChild(childAAB, RelationshipTypeEnum.ISA);
TermConcept childAB = new TermConcept(cs, "childAB");
parentA.addChild(childAB, RelationshipTypeEnum.ISA);
TermConcept parentB = new TermConcept(cs, "ParentB");
cs.getConcepts().add(parentB);
myTermSvc.storeNewCodeSystemVersion(table.getId(), URL_MY_CODE_SYSTEM, cs);
createLocalVs(codeSystem);
createExternalCsAndLocalVs();
Observation obsPA = new Observation();
obsPA.getCode().addCoding().setSystem(URL_MY_CODE_SYSTEM).setCode("ParentA");
@ -195,6 +164,48 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test {
}
private void createExternalCsAndLocalVs() {
CodeSystem codeSystem = createExternalCs();
createLocalVs(codeSystem);
}
private CodeSystem createExternalCs() {
CodeSystem codeSystem = new CodeSystem();
codeSystem.setUrl(URL_MY_CODE_SYSTEM);
codeSystem.setContent(CodeSystemContentMode.NOTPRESENT);
IIdType id = myCodeSystemDao.create(codeSystem, new ServletRequestDetails()).getId().toUnqualified();
ResourceTable table = myResourceTableDao.findOne(id.getIdPartAsLong());
TermCodeSystemVersion cs = new TermCodeSystemVersion();
cs.setResource(table);
cs.setResourceVersionId(table.getVersion());
TermConcept parentA = new TermConcept(cs, "ParentA").setDisplay("Parent A");
cs.getConcepts().add(parentA);
TermConcept childAA = new TermConcept(cs, "childAA").setDisplay("Child AA");
parentA.addChild(childAA, RelationshipTypeEnum.ISA);
TermConcept childAAA = new TermConcept(cs, "childAAA").setDisplay("Child AAA");
childAA.addChild(childAAA, RelationshipTypeEnum.ISA);
TermConcept childAAB = new TermConcept(cs, "childAAB").setDisplay("Child AAB");
childAA.addChild(childAAB, RelationshipTypeEnum.ISA);
TermConcept childAB = new TermConcept(cs, "childAB").setDisplay("Child AB");
parentA.addChild(childAB, RelationshipTypeEnum.ISA);
TermConcept parentB = new TermConcept(cs, "ParentB").setDisplay("Parent B");
cs.getConcepts().add(parentB);
myTermSvc.storeNewCodeSystemVersion(table.getId(), URL_MY_CODE_SYSTEM, cs);
return codeSystem;
}
@Test
public void testSearchCodeBelowAndAboveUnknownCodeSystem() {
@ -299,19 +310,48 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test {
//
}
@Test
public void testExpandWithSystemAndCodesAndFilterInLocalValueSet() {
public void testExpandWithSystemAndCodesAndFilterInExternalValueSet() {
createExternalCsAndLocalVs();
ValueSet vs = new ValueSet();
ConceptSetComponent include = vs.getCompose().addInclude();
include.setSystem(URL_MY_CODE_SYSTEM);
include.addConcept().setCode("ParentA");
include.addConcept().setCode("childAA");
include.addConcept().setCode("childAAA");
include.addFilter().setProperty("display").setOp(FilterOperator.EQUAL).setValue("Parent B");
ValueSet result = myValueSetDao.expand(vs, null);
String encoded = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(result);
ourLog.info(encoded);
ArrayList<String> codes = toCodesContains(result.getExpansion().getContains());
assertThat(codes, containsInAnyOrder("ParentA", "childAA", "childAAA", "ParentB"));
int idx = codes.indexOf("childAA");
assertEquals("childAA", result.getExpansion().getContains().get(idx).getCode());
assertEquals("Child AA", result.getExpansion().getContains().get(idx).getDisplay());
assertEquals(URL_MY_CODE_SYSTEM, result.getExpansion().getContains().get(idx).getSystem());
}
@Test
public void testExpandWithSystemAndCodesAndFilterKeywordInLocalValueSet() {
createLocalCsAndVs();
ValueSet vs = new ValueSet();
ConceptSetComponent include = vs.getCompose().addInclude();
include.setSystem(URL_MY_CODE_SYSTEM);
include.addConcept().setCode("A");
include.addConcept().setCode("AA");
include.addConcept().setCode("AAA");
include.addConcept().setCode("AB");
// include.addConcept().setCode("A");
// include.addConcept().setCode("AA");
// include.addConcept().setCode("AAA");
// include.addConcept().setCode("AB");
include.addFilter().setProperty("display").setOp(FilterOperator.EQUAL).setValue("Code AAA");
include.addFilter().setProperty("display").setOp(FilterOperator.EQUAL).setValue("AAA");
ValueSet result = myValueSetDao.expand(vs, null);
@ -324,11 +364,44 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test {
assertEquals("AAA", result.getExpansion().getContains().get(0).getCode());
assertEquals("Code AAA", result.getExpansion().getContains().get(0).getDisplay());
assertEquals(URL_MY_CODE_SYSTEM, result.getExpansion().getContains().get(0).getSystem());
// ValueSet expansion = myValueSetDao.expandByIdentifier(URL_MY_VALUE_SET, "cervical");
// ValueSet expansion = myValueSetDao.expandByIdentifier(URL_MY_VALUE_SET, "cervical");
//
}
@Test
public void testExpandWithNoResultsInLocalValueSet1() {
createLocalCsAndVs();
ValueSet vs = new ValueSet();
ConceptSetComponent include = vs.getCompose().addInclude();
include.setSystem(URL_MY_CODE_SYSTEM);
include.addConcept().setCode("ZZZZ");
try {
myValueSetDao.expand(vs, null);
fail();
} catch (InvalidRequestException e) {
assertEquals("Unable to find code 'ZZZZ' in code system http://example.com/my_code_system", e.getMessage());
}
}
@Test
public void testExpandWithNoResultsInLocalValueSet2() {
createLocalCsAndVs();
ValueSet vs = new ValueSet();
ConceptSetComponent include = vs.getCompose().addInclude();
include.setSystem(URL_MY_CODE_SYSTEM + "AA");
include.addConcept().setCode("A");
try {
myValueSetDao.expand(vs, null);
fail();
} catch (InvalidRequestException e) {
assertEquals("unable to find code system http://example.com/my_code_systemAA", e.getMessage());
}
}
private ArrayList<String> toCodesContains(List<ValueSetExpansionContainsComponent> theContains) {
ArrayList<String> retVal = new ArrayList<String>();
for (ValueSetExpansionContainsComponent next : theContains) {

View File

@ -158,23 +158,19 @@ public class FhirResourceDaoDstu3ValueSetTest extends BaseJpaDstu3Test {
ValueSet expanded = myValueSetDao.expand(myExtensionalVsId, null);
resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded);
ourLog.info(resp);
// @formatter:off
assertThat(resp,
stringContainsInOrder("<ValueSet xmlns=\"http://hl7.org/fhir\">",
"<expansion>",
"<contains>",
"<system value=\"http://acme.org\"/>",
"<code value=\"8450-9\"/>",
"<display value=\"Systolic blood pressure--expiration\"/>",
"</contains>",
"<contains>",
"<system value=\"http://acme.org\"/>",
"<code value=\"11378-7\"/>",
"<display value=\"Systolic blood pressure at First encounter\"/>",
"</contains>",
"</expansion>"
));
//@formatter:on
assertThat(resp, containsString("<ValueSet xmlns=\"http://hl7.org/fhir\">"));
assertThat(resp, containsString("<expansion>"));
assertThat(resp, containsString("<contains>"));
assertThat(resp, containsString("<system value=\"http://acme.org\"/>"));
assertThat(resp, containsString("<code value=\"8450-9\"/>"));
assertThat(resp, containsString("<display value=\"Systolic blood pressure--expiration\"/>"));
assertThat(resp, containsString("</contains>"));
assertThat(resp, containsString("<contains>"));
assertThat(resp, containsString("<system value=\"http://acme.org\"/>"));
assertThat(resp, containsString("<code value=\"11378-7\"/>"));
assertThat(resp, containsString("<display value=\"Systolic blood pressure at First encounter\"/>"));
assertThat(resp, containsString("</contains>"));
assertThat(resp, containsString("</expansion>"));
/*
* Filter with display name

View File

@ -2830,23 +2830,19 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
String resp = IOUtils.toString(response.getEntity().getContent());
ourLog.info(resp);
assertEquals(200, response.getStatusLine().getStatusCode());
// @formatter:off
assertThat(resp,
stringContainsInOrder("<ValueSet xmlns=\"http://hl7.org/fhir\">",
"<expansion>",
"<contains>",
"<system value=\"http://acme.org\"/>",
"<code value=\"8450-9\"/>",
"<display value=\"Systolic blood pressure--expiration\"/>",
"</contains>",
"<contains>",
"<system value=\"http://acme.org\"/>",
"<code value=\"11378-7\"/>",
"<display value=\"Systolic blood pressure at First encounter\"/>",
"</contains>",
"</expansion>"
));
//@formatter:on
assertThat(resp, containsString("<ValueSet xmlns=\"http://hl7.org/fhir\">"));
assertThat(resp, containsString("<expansion>"));
assertThat(resp, containsString("<contains>"));
assertThat(resp, containsString("<system value=\"http://acme.org\"/>"));
assertThat(resp, containsString("<code value=\"8450-9\"/>"));
assertThat(resp, containsString("<display value=\"Systolic blood pressure--expiration\"/>"));
assertThat(resp, containsString("</contains>"));
assertThat(resp, containsString("<contains>"));
assertThat(resp, containsString("<system value=\"http://acme.org\"/>"));
assertThat(resp, containsString("<code value=\"11378-7\"/>"));
assertThat(resp, containsString("<display value=\"Systolic blood pressure at First encounter\"/>"));
assertThat(resp, containsString("</contains>"));
assertThat(resp, containsString("</expansion>"));
} finally {
IOUtils.closeQuietly(response.getEntity().getContent());
response.close();

View File

@ -62,23 +62,19 @@ public class ResourceProviderDstu3ValueSetTest extends BaseResourceProviderDstu3
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded);
ourLog.info(resp);
// @formatter:off
assertThat(resp,
stringContainsInOrder("<ValueSet xmlns=\"http://hl7.org/fhir\">",
"<expansion>",
"<contains>",
"<system value=\"http://acme.org\"/>",
"<code value=\"8450-9\"/>",
"<display value=\"Systolic blood pressure--expiration\"/>",
"</contains>",
"<contains>",
"<system value=\"http://acme.org\"/>",
"<code value=\"11378-7\"/>",
"<display value=\"Systolic blood pressure at First encounter\"/>",
"</contains>",
"</expansion>"
));
//@formatter:on
assertThat(resp, containsString("<ValueSet xmlns=\"http://hl7.org/fhir\">"));
assertThat(resp, containsString("<expansion>"));
assertThat(resp, containsString("<contains>"));
assertThat(resp, containsString("<system value=\"http://acme.org\"/>"));
assertThat(resp, containsString("<code value=\"8450-9\"/>"));
assertThat(resp, containsString("<display value=\"Systolic blood pressure--expiration\"/>"));
assertThat(resp, containsString("</contains>"));
assertThat(resp, containsString("<contains>"));
assertThat(resp, containsString("<system value=\"http://acme.org\"/>"));
assertThat(resp, containsString("<code value=\"11378-7\"/>"));
assertThat(resp, containsString("<display value=\"Systolic blood pressure at First encounter\"/>"));
assertThat(resp, containsString("</contains>"));
assertThat(resp, containsString("</expansion>"));
/*
* Filter with display name

View File

@ -0,0 +1,10 @@
"LOINC_NUM","COMPONENT","PROPERTY","TIME_ASPCT","SYSTEM","SCALE_TYP","METHOD_TYP","CLASS","SOURCE","VersionLastChanged","CHNG_TYPE","DefinitionDescription","STATUS","CONSUMER_NAME","CLASSTYPE","FORMULA","SPECIES","EXMPL_ANSWERS","SURVEY_QUEST_TEXT","SURVEY_QUEST_SRC","UNITSREQUIRED","SUBMITTED_UNITS","RELATEDNAMES2","SHORTNAME","ORDER_OBS","CDISC_COMMON_TESTS","HL7_FIELD_SUBFIELD_ID","EXTERNAL_COPYRIGHT_NOTICE","EXAMPLE_UNITS","LONG_COMMON_NAME","UnitsAndRange","DOCUMENT_SECTION","EXAMPLE_UCUM_UNITS","EXAMPLE_SI_UCUM_UNITS","STATUS_REASON","STATUS_TEXT","CHANGE_REASON_PUBLIC","COMMON_TEST_RANK","COMMON_ORDER_RANK","COMMON_SI_TEST_RANK","HL7_ATTACHMENT_STRUCTURE","EXTERNAL_COPYRIGHT_LINK","PanelType","AskAtOrderEntry","AssociatedObservations"
"10013-1","R' wave amplitude.lead I","Elpot","Pt","Heart","Qn","EKG","EKG.MEAS","CH","2.48","MIN",,"ACTIVE",,2,,,,,,"Y",,"Cardiac; ECG; EKG.MEASUREMENTS; Electrical potential; Electrocardiogram; Electrocardiograph; Hrt; Painter's colic; PB; Plumbism; Point in time; QNT; Quan; Quant; Quantitative; R prime; R' wave Amp L-I; R wave Amp L-I; Random; Right; Voltage","R' wave Amp L-I","Observation",,,,"mV","R' wave amplitude in lead I",,,"mV",,,,,0,0,0,,,,,
"10014-9","R' wave amplitude.lead II","Elpot","Pt","Heart","Qn","EKG","EKG.MEAS","CH","2.48","MIN",,"ACTIVE",,2,,,,,,"Y",,"2; Cardiac; ECG; EKG.MEASUREMENTS; Electrical potential; Electrocardiogram; Electrocardiograph; Hrt; Painter's colic; PB; Plumbism; Point in time; QNT; Quan; Quant; Quantitative; R prime; R' wave Amp L-II; R wave Amp L-II; Random; Right; Voltage","R' wave Amp L-II","Observation",,,,"mV","R' wave amplitude in lead II",,,"mV",,,,,0,0,0,,,,,
"10015-6","R' wave amplitude.lead III","Elpot","Pt","Heart","Qn","EKG","EKG.MEAS","CH","2.48","MIN",,"ACTIVE",,2,,,,,,"Y",,"3; Cardiac; ECG; EKG.MEASUREMENTS; Electrical potential; Electrocardiogram; Electrocardiograph; Hrt; Painter's colic; PB; Plumbism; Point in time; QNT; Quan; Quant; Quantitative; R prime; R' wave Amp L-III; R wave Amp L-III; Random; Right; Voltage","R' wave Amp L-III","Observation",,,,"mV","R' wave amplitude in lead III",,,"mV",,,,,0,0,0,,,,,
"10016-4","R' wave amplitude.lead V1","Elpot","Pt","Heart","Qn","EKG","EKG.MEAS","CH","2.48","MIN",,"ACTIVE",,2,,,,,,"Y",,"Cardiac; ECG; EKG.MEASUREMENTS; Electrical potential; Electrocardiogram; Electrocardiograph; Hrt; Painter's colic; PB; Plumbism; Point in time; QNT; Quan; Quant; Quantitative; R prime; R' wave Amp L-V1; R wave Amp L-V1; Random; Right; Voltage","R' wave Amp L-V1","Observation",,,,"mV","R' wave amplitude in lead V1",,,"mV",,,,,0,0,0,,,,,
"1001-7","DBG Ab","Pr","Pt","Ser/Plas^donor","Ord",,"BLDBK","FS","2.44","MIN",,"ACTIVE",,1,,,,,,,,"ABS; Aby; Antby; Anti; Antibodies; Antibody; Autoantibodies; Autoantibody; BLOOD BANK; Donna Bennett-Goodspeed; Donr; Ordinal; Pl; Plasma; Plsm; Point in time; QL; Qual; Qualitative; Random; Screen; SerP; SerPl; SerPl^donor; SerPlas; Serum; Serum or plasma; SR","DBG Ab SerPl Donr Ql","Observation",,,,,"DBG Ab [Presence] in Serum or Plasma from donor",,,,,,,"The Property has been changed from ACnc to Pr (Presence) to reflect the new model for ordinal terms where results are based on presence or absence.",0,0,0,,,,,
"10017-2","R' wave amplitude.lead V2","Elpot","Pt","Heart","Qn","EKG","EKG.MEAS","CH","2.48","MIN",,"ACTIVE",,2,,,,,,"Y",,"Cardiac; ECG; EKG.MEASUREMENTS; Electrical potential; Electrocardiogram; Electrocardiograph; Hrt; Painter's colic; PB; Plumbism; Point in time; QNT; Quan; Quant; Quantitative; R prime; R' wave Amp L-V2; R wave Amp L-V2; Random; Right; Voltage","R' wave Amp L-V2","Observation",,,,"mV","R' wave amplitude in lead V2",,,"mV",,,,,0,0,0,,,,,
"10018-0","R' wave amplitude.lead V3","Elpot","Pt","Heart","Qn","EKG","EKG.MEAS","CH","2.48","MIN",,"ACTIVE",,2,,,,,,"Y",,"Cardiac; ECG; EKG.MEASUREMENTS; Electrical potential; Electrocardiogram; Electrocardiograph; Hrt; Painter's colic; PB; Plumbism; Point in time; QNT; Quan; Quant; Quantitative; R prime; R' wave Amp L-V3; R wave Amp L-V3; Random; Right; Voltage","R' wave Amp L-V3","Observation",,,,"mV","R' wave amplitude in lead V3",,,"mV",,,,,0,0,0,,,,,
"10019-8","R' wave amplitude.lead V4","Elpot","Pt","Heart","Qn","EKG","EKG.MEAS","CH","2.48","MIN",,"ACTIVE",,2,,,,,,"Y",,"Cardiac; ECG; EKG.MEASUREMENTS; Electrical potential; Electrocardiogram; Electrocardiograph; Hrt; Painter's colic; PB; Plumbism; Point in time; QNT; Quan; Quant; Quantitative; R prime; R' wave Amp L-V4; R wave Amp L-V4; Random; Right; Voltage","R' wave Amp L-V4","Observation",,,,"mV","R' wave amplitude in lead V4",,,"mV",,,,,0,0,0,,,,,
"10020-6","R' wave amplitude.lead V5","Elpot","Pt","Heart","Qn","EKG","EKG.MEAS","CH","2.48","MIN",,"ACTIVE",,2,,,,,,"Y",,"Cardiac; ECG; EKG.MEASUREMENTS; Electrical potential; Electrocardiogram; Electrocardiograph; Hrt; Painter's colic; PB; Plumbism; Point in time; QNT; Quan; Quant; Quantitative; R prime; R' wave Amp L-V5; R wave Amp L-V5; Random; Right; Voltage","R' wave Amp L-V5","Observation",,,,"mV","R' wave amplitude in lead V5",,,"mV",,,,,0,0,0,,,,,
1 LOINC_NUM COMPONENT PROPERTY TIME_ASPCT SYSTEM SCALE_TYP METHOD_TYP CLASS SOURCE VersionLastChanged CHNG_TYPE DefinitionDescription STATUS CONSUMER_NAME CLASSTYPE FORMULA SPECIES EXMPL_ANSWERS SURVEY_QUEST_TEXT SURVEY_QUEST_SRC UNITSREQUIRED SUBMITTED_UNITS RELATEDNAMES2 SHORTNAME ORDER_OBS CDISC_COMMON_TESTS HL7_FIELD_SUBFIELD_ID EXTERNAL_COPYRIGHT_NOTICE EXAMPLE_UNITS LONG_COMMON_NAME UnitsAndRange DOCUMENT_SECTION EXAMPLE_UCUM_UNITS EXAMPLE_SI_UCUM_UNITS STATUS_REASON STATUS_TEXT CHANGE_REASON_PUBLIC COMMON_TEST_RANK COMMON_ORDER_RANK COMMON_SI_TEST_RANK HL7_ATTACHMENT_STRUCTURE EXTERNAL_COPYRIGHT_LINK PanelType AskAtOrderEntry AssociatedObservations
2 10013-1 R' wave amplitude.lead I Elpot Pt Heart Qn EKG EKG.MEAS CH 2.48 MIN ACTIVE 2 Y Cardiac; ECG; EKG.MEASUREMENTS; Electrical potential; Electrocardiogram; Electrocardiograph; Hrt; Painter's colic; PB; Plumbism; Point in time; QNT; Quan; Quant; Quantitative; R prime; R' wave Amp L-I; R wave Amp L-I; Random; Right; Voltage R' wave Amp L-I Observation mV R' wave amplitude in lead I mV 0 0 0
3 10014-9 R' wave amplitude.lead II Elpot Pt Heart Qn EKG EKG.MEAS CH 2.48 MIN ACTIVE 2 Y 2; Cardiac; ECG; EKG.MEASUREMENTS; Electrical potential; Electrocardiogram; Electrocardiograph; Hrt; Painter's colic; PB; Plumbism; Point in time; QNT; Quan; Quant; Quantitative; R prime; R' wave Amp L-II; R wave Amp L-II; Random; Right; Voltage R' wave Amp L-II Observation mV R' wave amplitude in lead II mV 0 0 0
4 10015-6 R' wave amplitude.lead III Elpot Pt Heart Qn EKG EKG.MEAS CH 2.48 MIN ACTIVE 2 Y 3; Cardiac; ECG; EKG.MEASUREMENTS; Electrical potential; Electrocardiogram; Electrocardiograph; Hrt; Painter's colic; PB; Plumbism; Point in time; QNT; Quan; Quant; Quantitative; R prime; R' wave Amp L-III; R wave Amp L-III; Random; Right; Voltage R' wave Amp L-III Observation mV R' wave amplitude in lead III mV 0 0 0
5 10016-4 R' wave amplitude.lead V1 Elpot Pt Heart Qn EKG EKG.MEAS CH 2.48 MIN ACTIVE 2 Y Cardiac; ECG; EKG.MEASUREMENTS; Electrical potential; Electrocardiogram; Electrocardiograph; Hrt; Painter's colic; PB; Plumbism; Point in time; QNT; Quan; Quant; Quantitative; R prime; R' wave Amp L-V1; R wave Amp L-V1; Random; Right; Voltage R' wave Amp L-V1 Observation mV R' wave amplitude in lead V1 mV 0 0 0
6 1001-7 DBG Ab Pr Pt Ser/Plas^donor Ord BLDBK FS 2.44 MIN ACTIVE 1 ABS; Aby; Antby; Anti; Antibodies; Antibody; Autoantibodies; Autoantibody; BLOOD BANK; Donna Bennett-Goodspeed; Donr; Ordinal; Pl; Plasma; Plsm; Point in time; QL; Qual; Qualitative; Random; Screen; SerP; SerPl; SerPl^donor; SerPlas; Serum; Serum or plasma; SR DBG Ab SerPl Donr Ql Observation DBG Ab [Presence] in Serum or Plasma from donor The Property has been changed from ACnc to Pr (Presence) to reflect the new model for ordinal terms where results are based on presence or absence. 0 0 0
7 10017-2 R' wave amplitude.lead V2 Elpot Pt Heart Qn EKG EKG.MEAS CH 2.48 MIN ACTIVE 2 Y Cardiac; ECG; EKG.MEASUREMENTS; Electrical potential; Electrocardiogram; Electrocardiograph; Hrt; Painter's colic; PB; Plumbism; Point in time; QNT; Quan; Quant; Quantitative; R prime; R' wave Amp L-V2; R wave Amp L-V2; Random; Right; Voltage R' wave Amp L-V2 Observation mV R' wave amplitude in lead V2 mV 0 0 0
8 10018-0 R' wave amplitude.lead V3 Elpot Pt Heart Qn EKG EKG.MEAS CH 2.48 MIN ACTIVE 2 Y Cardiac; ECG; EKG.MEASUREMENTS; Electrical potential; Electrocardiogram; Electrocardiograph; Hrt; Painter's colic; PB; Plumbism; Point in time; QNT; Quan; Quant; Quantitative; R prime; R' wave Amp L-V3; R wave Amp L-V3; Random; Right; Voltage R' wave Amp L-V3 Observation mV R' wave amplitude in lead V3 mV 0 0 0
9 10019-8 R' wave amplitude.lead V4 Elpot Pt Heart Qn EKG EKG.MEAS CH 2.48 MIN ACTIVE 2 Y Cardiac; ECG; EKG.MEASUREMENTS; Electrical potential; Electrocardiogram; Electrocardiograph; Hrt; Painter's colic; PB; Plumbism; Point in time; QNT; Quan; Quant; Quantitative; R prime; R' wave Amp L-V4; R wave Amp L-V4; Random; Right; Voltage R' wave Amp L-V4 Observation mV R' wave amplitude in lead V4 mV 0 0 0
10 10020-6 R' wave amplitude.lead V5 Elpot Pt Heart Qn EKG EKG.MEAS CH 2.48 MIN ACTIVE 2 Y Cardiac; ECG; EKG.MEASUREMENTS; Electrical potential; Electrocardiogram; Electrocardiograph; Hrt; Painter's colic; PB; Plumbism; Point in time; QNT; Quan; Quant; Quantitative; R prime; R' wave Amp L-V5; R wave Amp L-V5; Random; Right; Voltage R' wave Amp L-V5 Observation mV R' wave amplitude in lead V5 mV 0 0 0

View File

@ -11,6 +11,7 @@ import java.util.Set;
import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.dstu3.exceptions.TerminologyServiceException;
import org.hl7.fhir.dstu3.formats.IParser;
import org.hl7.fhir.dstu3.formats.ParserType;
import org.hl7.fhir.dstu3.hapi.validation.IValidationSupport.CodeValidationResult;

View File

@ -1,5 +1,7 @@
package org.hl7.fhir.dstu3.terminologies;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import java.io.FileNotFoundException;
import java.io.IOException;
@ -46,6 +48,7 @@ import org.hl7.fhir.dstu3.model.Type;
import org.hl7.fhir.dstu3.model.UriType;
import org.hl7.fhir.dstu3.model.ValueSet;
import org.hl7.fhir.dstu3.model.CodeSystem;
import org.hl7.fhir.dstu3.model.CodeSystem.CodeSystemContentMode;
import org.hl7.fhir.dstu3.model.CodeSystem.ConceptDefinitionComponent;
import org.hl7.fhir.dstu3.model.ValueSet.ConceptReferenceComponent;
import org.hl7.fhir.dstu3.model.ValueSet.ConceptSetComponent;
@ -144,20 +147,16 @@ public class ValueSetExpanderSimple implements ValueSetExpander {
}
private void includeCodes(ConceptSetComponent inc, List<ValueSetExpansionParameterComponent> params) throws TerminologyServiceException, ETooCostly {
if (context.supportsSystem(inc.getSystem())) {
try {
int i = codes.size();
CodeSystem cs = context.fetchCodeSystem(inc.getSystem());
if ((cs == null || cs.getContent() != CodeSystemContentMode.COMPLETE) && context.supportsSystem(inc.getSystem())) {
addCodes(context.expandVS(inc), params);
if (codes.size() > i)
return;
} catch (Exception e) {
// ok, we'll try locally
}
return;
}
CodeSystem cs = context.fetchCodeSystem(inc.getSystem());
if (cs == null)
throw new TerminologyServiceException("unable to find code system "+inc.getSystem().toString());
if (cs == null)
throw new TerminologyServiceException("unable to find code system "+inc.getSystem().toString());
if (cs.getContent() != CodeSystemContentMode.COMPLETE)
throw new TerminologyServiceException("Code system "+inc.getSystem().toString()+" is incomplete");
if (cs.hasVersion())
if (!existsInParams(params, "version", new UriType(cs.getUrl()+"?version="+cs.getVersion())))
params.add(new ValueSetExpansionParameterComponent().setName("version").setValue(new UriType(cs.getUrl()+"?version="+cs.getVersion())));
@ -181,8 +180,17 @@ public class ValueSetExpanderSimple implements ValueSetExpander {
if (def == null)
throw new TerminologyServiceException("Code '"+fc.getValue()+"' not found in system '"+inc.getSystem()+"'");
addCodeAndDescendents(cs, inc.getSystem(), def);
} else if ("display".equals(fc.getProperty()) && fc.getOp() == FilterOperator.EQUAL) {
ConceptDefinitionComponent def = getConceptForCode(cs.getConcept(), fc.getValue());
if (def != null) {
if (isNotBlank(def.getDisplay()) && isNotBlank(fc.getValue())) {
if (def.getDisplay().contains(fc.getValue())) {
addCode(inc.getSystem(), def.getCode(), def.getDisplay());
}
}
}
} else
throw new NotImplementedException("not done yet");
throw new NotImplementedException("Search by property[" + fc.getProperty() + "] and op[" + fc.getOp() + "] is not supported yet");
}
}