package edu.gatech.i3l.fhir.jpa.query; import static org.apache.commons.lang3.StringUtils.isBlank; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import javax.persistence.EntityManager; import javax.persistence.TypedQuery; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.From; import javax.persistence.criteria.JoinType; import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import ca.uhn.fhir.context.BaseRuntimeChildDefinition; import ca.uhn.fhir.context.ConfigurationException; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.RuntimeChildResourceDefinition; import ca.uhn.fhir.context.RuntimeResourceDefinition; import ca.uhn.fhir.context.RuntimeSearchParam; import ca.uhn.fhir.model.api.IPrimitiveDatatype; import ca.uhn.fhir.model.api.IQueryParameterType; import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.base.composite.BaseCodingDt; import ca.uhn.fhir.model.base.composite.BaseIdentifierDt; import ca.uhn.fhir.model.base.composite.BaseQuantityDt; import ca.uhn.fhir.model.dstu.valueset.QuantityCompararatorEnum; import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.rest.method.RestSearchParameterTypeEnum; import ca.uhn.fhir.rest.param.CompositeParam; import ca.uhn.fhir.rest.param.DateParam; import ca.uhn.fhir.rest.param.DateRangeParam; import ca.uhn.fhir.rest.param.NumberParam; import ca.uhn.fhir.rest.param.ParamPrefixEnum; import ca.uhn.fhir.rest.param.QuantityParam; import ca.uhn.fhir.rest.param.ReferenceParam; import ca.uhn.fhir.rest.param.StringParam; import ca.uhn.fhir.rest.param.TokenParam; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import edu.gatech.i3l.fhir.jpa.dao.BaseFhirDao; import edu.gatech.i3l.fhir.jpa.dao.IFhirDao; import edu.gatech.i3l.fhir.jpa.dao.IFhirResourceDao; import edu.gatech.i3l.fhir.jpa.entity.IResourceEntity; public class QueryHelper { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(QueryHelper.class); private EntityManager myEntityManager; private Class<? extends IResourceEntity> myResourceEntity; private Class<? extends IResource> myResourceType; private FhirContext myContext; private PredicateBuilder predicateBuilder; private IFhirDao baseFhirDao; public QueryHelper() { super(); } public QueryHelper(PredicateBuilder predicateBuilder){ this.predicateBuilder = predicateBuilder; } public QueryHelper( EntityManager theEntityManager, Class<? extends IResourceEntity> theResourceEntity, Class<? extends IResource> theResourceType, FhirContext theContext, BaseFhirDao theBaseFhirDao) { super(); this.myEntityManager = theEntityManager; this.myResourceEntity = theResourceEntity; this.myResourceType = theResourceType; this.myContext = theContext; this.baseFhirDao = theBaseFhirDao; } public PredicateBuilder getPredicateBuilder() { return predicateBuilder; } public void setPredicateBuilder(PredicateBuilder predicateBuilder) { this.predicateBuilder = predicateBuilder; } public Set<Long> searchById(Set<Long> theExistingPids, Set<Long> thePids) { if (thePids == null || thePids.isEmpty()) { return Collections.emptySet(); } CriteriaBuilder builder = myEntityManager.getCriteriaBuilder(); CriteriaQuery<Long> cq = builder.createQuery(Long.class); Root<? extends IResourceEntity> from = cq.from(myResourceEntity); cq.select(from.get("id").as(Long.class)); Predicate idPrecidate = from.get("id").in(thePids); Predicate addPredicate = predicateBuilder.addCommonPredicate(builder, from); if(addPredicate != null){ Predicate and = builder.and(idPrecidate, addPredicate); cq.where(and); } else { cq.where(idPrecidate); } TypedQuery<Long> q = myEntityManager.createQuery(cq); HashSet<Long> found = new HashSet<Long>(q.getResultList()); if (!theExistingPids.isEmpty()) { theExistingPids.retainAll(found); } return found; } public Set<Long> searchByDate(String theParamName, Set<Long> thePids, List<? extends IQueryParameterType> theList) { if (theList == null || theList.isEmpty()) { return thePids; } // if (Boolean.TRUE.equals(theList.get(0).getMissing())) { // return addPredicateParamMissing(thePids, "myParamsDate", theParamName, ResourceIndexedSearchParamDate.class); // } CriteriaBuilder builder = myEntityManager.getCriteriaBuilder(); CriteriaQuery<Long> cq = builder.createQuery(Long.class); Root<? extends IResourceEntity> from = cq.from(myResourceEntity); cq.select(from.get("id").as(Long.class)); // Root<ResourceIndexedSearchParamDate> from = cq.from(ResourceIndexedSearchParamDate.class); // cq.select(from.get("myResourcePid").as(Long.class)); // List<Predicate> codePredicates = new ArrayList<Predicate>(); for (IQueryParameterType nextOr : theList) { // if (addPredicateMissingFalseIfPresent(builder, theParamName, from, codePredicates, nextOr)) {//WARNING check his line for correctness // continue; // } IQueryParameterType params = toParameterType(myContext.getResourceDefinition(myResourceType).getSearchParam(theParamName), nextOr.getValueAsQueryToken(myContext)); Predicate p = createPredicateDate(builder, from, theParamName, params); codePredicates.add(p); } Predicate masterCodePredicate = builder.or(codePredicates.toArray(new Predicate[0])); Predicate addPredicate = predicateBuilder.addCommonPredicate(builder, from); if (thePids.size() > 0) { Predicate inPids = (from.get("id").in(thePids)); if(addPredicate != null){ cq.where(builder.and(inPids, masterCodePredicate, addPredicate)); } else { cq.where(builder.and(inPids, masterCodePredicate)); } } else { if(addPredicate != null){ cq.where(builder.and(masterCodePredicate, addPredicate)); } else { cq.where(builder.and(masterCodePredicate)); } } TypedQuery<Long> q = myEntityManager.createQuery(cq); return new HashSet<Long>(q.getResultList()); } protected Predicate createPredicateDate(CriteriaBuilder theBuilder, From<? extends IResourceEntity, ? extends IResourceEntity> from, String theParamName, IQueryParameterType theParam) { Predicate p; if (theParam instanceof DateParam) { DateParam date = (DateParam) theParam; if (!date.isEmpty()) { DateRangeParam range = new DateRangeParam(date); p = predicateBuilder.translatePredicateDate(myResourceEntity, theBuilder, from, range, theParamName, theParam); } else { // From original method: TODO: handle missing date param? p = null; } } else if (theParam instanceof DateRangeParam) { DateRangeParam range = (DateRangeParam) theParam; p = predicateBuilder.translatePredicateDate(myResourceEntity, theBuilder, from, range, theParamName, theParam); } else { throw new IllegalArgumentException("Invalid token type: " + theParam.getClass()); } return p; } private Predicate createPredicateString(IQueryParameterType theParameter, String theParamName, CriteriaBuilder theBuilder, From<? extends IResourceEntity, ? extends IResourceEntity> stringJoin) { String rawSearchTerm; if (theParameter instanceof TokenParam) { TokenParam id = (TokenParam) theParameter; if (!id.isText()) { throw new IllegalStateException("Trying to process a text search on a non-text token parameter"); } rawSearchTerm = id.getValue(); } else if (theParameter instanceof StringParam) { StringParam id = (StringParam) theParameter; rawSearchTerm = id.getValue(); } else if (theParameter instanceof IPrimitiveDatatype<?>) { IPrimitiveDatatype<?> id = (IPrimitiveDatatype<?>) theParameter; rawSearchTerm = id.getValueAsString(); } else { throw new IllegalArgumentException("Invalid token type: " + theParameter.getClass()); } String likeExpression = QueryUtilities.normalizeString(rawSearchTerm); likeExpression = "%" + likeExpression.replace("%", "[%]") + "%"; Predicate singleCode = predicateBuilder.translatePredicateString(myResourceEntity, theParamName, likeExpression, stringJoin, theBuilder); // Predicate singleCode = theBuilder.like(from.get("myValueNormalized").as(String.class), likeExpression); // Predicate singleCode = theBuilder.like(from.get("myValueNormalized").as(String.class), likeExpression); // if (theParameter instanceof StringParam && ((StringParam) theParameter).isExact()) { // Predicate exactCode = theBuilder.equal(from.get("myValueExact"), rawSearchTerm); // singleCode = theBuilder.and(singleCode, exactCode); // } return singleCode; } public Set<Long> searchByString(String theParamName, Set<Long> thePids, List<? extends IQueryParameterType> theList) { if (theList == null || theList.isEmpty()) { return thePids; } if (Boolean.TRUE.equals(theList.get(0).getMissing())) { // return addPredicateParamMissing(thePids, "myParamsString", theParamName, getResourceTable()); System.err.println(); //WARNING } CriteriaBuilder builder = myEntityManager.getCriteriaBuilder(); CriteriaQuery<Long> cq = builder.createQuery(Long.class); Root<? extends IResourceEntity> from = cq.from(myResourceEntity); cq.select(from.get("id").as(Long.class)); List<Predicate> codePredicates = new ArrayList<Predicate>(); for (IQueryParameterType nextOr : theList) { IQueryParameterType theParameter = nextOr; // if (addPredicateMissingFalseIfPresent(builder, theParamName, from, codePredicates, nextOr)) {//WARNING check his line for correctness // continue; // } Predicate singleCode = createPredicateString(theParameter, theParamName, builder, from); codePredicates.add(singleCode); } Predicate masterCodePredicate = builder.or(codePredicates.toArray(new Predicate[0])); Predicate addPredicate = predicateBuilder.addCommonPredicate(builder, from); if (thePids.size() > 0) { Predicate inPids = (from.get("id").in(thePids)); if(addPredicate != null){ cq.where(builder.and(masterCodePredicate, inPids, addPredicate)); } else { cq.where(builder.and(masterCodePredicate, inPids)); } } else { if(addPredicate != null){ cq.where(builder.and(masterCodePredicate, addPredicate)); } else { cq.where(builder.and(masterCodePredicate)); } } TypedQuery<Long> q = myEntityManager.createQuery(cq); return new HashSet<Long>(q.getResultList()); } public Set<Long> searchByReference(String theParamName, Set<Long> thePids, List<? extends IQueryParameterType> theList) { assert theParamName.contains(".") == false; Set<Long> pidsToRetain = thePids; if (theList == null || theList.isEmpty()) { return pidsToRetain; } // if (Boolean.TRUE.equals(theList.get(0).getMissing())) { // return addPredicateParamMissingResourceLink(thePids, "myResourceLinks", theParamName); // } CriteriaBuilder builder = myEntityManager.getCriteriaBuilder(); CriteriaQuery<Long> cq = builder.createQuery(Long.class); Root<? extends IResourceEntity> from = cq.from(myResourceEntity); cq.select(from.get("id").as(Long.class)); List<Predicate> codePredicates = new ArrayList<Predicate>(); for (IQueryParameterType nextOr : theList) { IQueryParameterType params = nextOr; // if (addPredicateMissingFalseIfPresentForResourceLink(builder, theParamName, from, codePredicates, nextOr)) { // continue; // } if (params instanceof ReferenceParam) { ReferenceParam ref = (ReferenceParam) params; String resourceId = ref.getValueAsQueryToken(myContext); if (isBlank(ref.getChain())) { if (resourceId.contains("/")) { IIdType dt = new IdDt(resourceId); resourceId = dt.getIdPart(); } Predicate eq = builder.equal(predicateBuilder.getPath(myResourceEntity, theParamName, from).get("id"), resourceId); codePredicates.add(eq); } else { String paramPath = myContext.getResourceDefinition(myResourceType).getSearchParam(theParamName).getPath(); BaseRuntimeChildDefinition def = myContext.newTerser().getDefinition(myResourceType, paramPath); if (!(def instanceof RuntimeChildResourceDefinition)) { throw new ConfigurationException("Property " + paramPath + " of type " + myResourceType + " is not a resource: " + def.getClass()); } List<Class<? extends IBaseResource>> resourceTypes; if (isBlank(ref.getResourceType())) { RuntimeChildResourceDefinition resDef = (RuntimeChildResourceDefinition) def; resourceTypes = resDef.getResourceTypes(); } else { resourceTypes = new ArrayList<Class<? extends IBaseResource>>(); RuntimeResourceDefinition resDef = myContext.getResourceDefinition(ref.getResourceType()); resourceTypes.add(resDef.getImplementingClass()); } boolean foundChainMatch = false; for (Class<? extends IBaseResource> nextType : resourceTypes) { RuntimeResourceDefinition typeDef = myContext.getResourceDefinition(nextType); String chain = ref.getChain(); String remainingChain = null; int chainDotIndex = chain.indexOf('.'); if (chainDotIndex != -1) { remainingChain = chain.substring(chainDotIndex + 1); chain = chain.substring(0, chainDotIndex); } RuntimeSearchParam param = typeDef.getSearchParam(chain); if (param == null) { ourLog.debug("Type {} doesn't have search param {}", nextType.getSimpleName(), param); continue; } IFhirResourceDao<?> dao = this.baseFhirDao.getDao(nextType); if (dao == null) { ourLog.debug("Don't have a DAO for type {}", nextType.getSimpleName(), param); continue; } IQueryParameterType chainValue; if (remainingChain != null) { if (param.getParamType() != RestSearchParameterTypeEnum.REFERENCE) { ourLog.debug("Type {} parameter {} is not a reference, can not chain {}", new Object[] { nextType.getSimpleName(), chain, remainingChain }); continue; } chainValue = new ReferenceParam(); chainValue.setValueAsQueryToken(null, resourceId); ((ReferenceParam) chainValue).setChain(remainingChain); } else { chainValue = toParameterType(param, resourceId); } foundChainMatch = true; Set<Long> pids = dao.searchForIds(chain, chainValue); if (pids.isEmpty()) { continue; } Predicate eq = predicateBuilder.getPath(myResourceEntity, theParamName, from).in(pids); // Predicate eq = builder.equal(predicateBuilder.getPath(myResourceEntity, theParamName, from).get("id"), resourceId); // Predicate eq = from.get("id").in(pids); codePredicates.add(eq); } if (!foundChainMatch) { throw new InvalidRequestException(myContext.getLocalizer().getMessage(BaseFhirDao.class, "invalidParameterChain", theParamName + '.' + ref.getChain())); } } } else { throw new IllegalArgumentException("Invalid token type (expecting ReferenceParam): " + params.getClass()); } } Predicate masterCodePredicate = builder.or(codePredicates.toArray(new Predicate[0])); //Predicate type = createResourceLinkPathPredicate(theParamName, builder, from); if (pidsToRetain.size() > 0) { Predicate inPids = (from.get("id").in(pidsToRetain)); cq.where(builder.and(//type, masterCodePredicate, inPids)); } else { cq.where(builder.and(//type, masterCodePredicate)); } TypedQuery<Long> q = myEntityManager.createQuery(cq); return new HashSet<Long>(q.getResultList()); } public Set<Long> searchByToken(String theParamName, Set<Long> thePids, List<? extends IQueryParameterType> theList) { if (theList == null || theList.isEmpty()) { return thePids; } // // if (Boolean.TRUE.equals(theList.get(0).getMissing())) { // return addPredicateParamMissing(thePids, "myParamsToken", theParamName, ResourceIndexedSearchParamToken.class); // } CriteriaBuilder builder = myEntityManager.getCriteriaBuilder(); CriteriaQuery<Long> cq = builder.createQuery(Long.class); Root<? extends IResourceEntity> from = cq.from(myResourceEntity); cq.select(from.get("id").as(Long.class)); List<Predicate> codePredicates = new ArrayList<Predicate>(); for (IQueryParameterType nextOr : theList) { // if (addPredicateMissingFalseIfPresent(builder, theParamName, from, codePredicates, nextOr)) { // continue; // } if (nextOr instanceof TokenParam) { TokenParam id = (TokenParam) nextOr; if (id.isText()) { return searchByString(theParamName, thePids, theList); } } Predicate singleCode = createPredicateToken(nextOr, theParamName, builder, from); codePredicates.add(singleCode); } Predicate masterCodePredicate = builder.or(codePredicates.toArray(new Predicate[0])); if (thePids.size() > 0) { Predicate inPids = (from.get("id").in(thePids)); cq.where(builder.and(masterCodePredicate, inPids)); } else { cq.where(builder.and(masterCodePredicate)); } TypedQuery<Long> q = myEntityManager.createQuery(cq); return new HashSet<Long>(q.getResultList()); } private Predicate createPredicateToken(IQueryParameterType theParameter, String theParamName, CriteriaBuilder theBuilder, From<? extends IResourceEntity, ? extends IResourceEntity> tokenJoin) { String code; String system; if (theParameter instanceof TokenParam) { TokenParam id = (TokenParam) theParameter; system = id.getSystem(); code = id.getValue(); } else if (theParameter instanceof BaseIdentifierDt) { BaseIdentifierDt id = (BaseIdentifierDt) theParameter; system = id.getSystemElement().getValueAsString(); code = id.getValueElement().getValue(); } else if (theParameter instanceof BaseCodingDt) { BaseCodingDt id = (BaseCodingDt) theParameter; system = id.getSystemElement().getValueAsString(); code = id.getCodeElement().getValue(); } else { throw new IllegalArgumentException("Invalid token type: " + theParameter.getClass()); } // if (system != null && system.length() > ResourceIndexedSearchParamToken.MAX_LENGTH) { // throw new InvalidRequestException("Parameter[" + theParamName + "] has system (" + system.length() + ") that is longer than maximum allowed (" + ResourceIndexedSearchParamToken.MAX_LENGTH // + "): " + system); // } // if (code != null && code.length() > ResourceIndexedSearchParamToken.MAX_LENGTH) { // throw new InvalidRequestException("Parameter[" + theParamName + "] has code (" + code.length() + ") that is longer than maximum allowed (" + ResourceIndexedSearchParamToken.MAX_LENGTH // + "): " + code); // } ArrayList<Predicate> singleCodePredicates = (new ArrayList<Predicate>()); Predicate predicateSystem = predicateBuilder.translatePredicateTokenSystem(myResourceEntity, theParamName, system, tokenJoin, theBuilder); if(predicateSystem != null) singleCodePredicates.add(predicateSystem); singleCodePredicates.add(predicateBuilder.translatePredicateTokenCode(myResourceEntity, theParamName, code, tokenJoin, theBuilder)); Predicate singleCode = theBuilder.and(singleCodePredicates.toArray(new Predicate[0])); return singleCode; } public Set<Long> searchByComposite(RuntimeSearchParam theParamDef, Set<Long> thePids, List<? extends IQueryParameterType> theNextAnd) { // TODO: fail if missing is set for a composite query CriteriaBuilder builder = myEntityManager.getCriteriaBuilder(); CriteriaQuery<Long> cq = builder.createQuery(Long.class); Root<? extends IResourceEntity> from = cq.from(myResourceEntity); cq.select(from.get("id").as(Long.class)); IQueryParameterType or = theNextAnd.get(0); if (!(or instanceof CompositeParam<?, ?>)) { throw new InvalidRequestException("Invalid type for composite param (must be " + CompositeParam.class.getSimpleName() + ": " + or.getClass()); } CompositeParam<?, ?> cp = (CompositeParam<?, ?>) or; RuntimeSearchParam left = theParamDef.getCompositeOf().get(0); IQueryParameterType leftValue = cp.getLeftValue(); Predicate leftPredicate = createCompositeParamPart(builder, from, left, leftValue); RuntimeSearchParam right = theParamDef.getCompositeOf().get(1); IQueryParameterType rightValue = cp.getRightValue(); Predicate rightPredicate = createCompositeParamPart(builder, from, right, rightValue); //Predicate type = builder.equal(from.get("myResourceType"), myResourceType); if (thePids.size() > 0) { Predicate inPids = (from.get("id").in(thePids)); cq.where(builder.and(//type, leftPredicate, rightPredicate, inPids)); } else { cq.where(builder.and(//type, leftPredicate, rightPredicate)); } TypedQuery<Long> q = myEntityManager.createQuery(cq); return new HashSet<Long>(q.getResultList()); } private Predicate createCompositeParamPart(CriteriaBuilder builder, Root<? extends IResourceEntity> from, RuntimeSearchParam left, IQueryParameterType leftValue) { Predicate retVal = null; From<? extends IResourceEntity, ? extends IResourceEntity> valueJoin = from.join("myParamsString", JoinType.INNER); switch (left.getParamType()) { case STRING: { retVal = createPredicateString(leftValue, left.getName(), builder, valueJoin); break; } case TOKEN: { retVal = createPredicateToken(leftValue, left.getName(), builder, valueJoin); break; } case DATE: { retVal = createPredicateDate(builder, valueJoin, left.getName(), leftValue); break; } } if (retVal == null) { throw new InvalidRequestException("Don't know how to handle composite parameter with type of " + left.getParamType()); } return retVal; } public Set<Long> searchByNumber(String theParamName, Set<Long> thePids, List<? extends IQueryParameterType> theList) { if (theList == null || theList.isEmpty()) { return thePids; } CriteriaBuilder builder = myEntityManager.getCriteriaBuilder(); CriteriaQuery<Long> cq = builder.createQuery(Long.class); Root<? extends IResourceEntity> from = cq.from(myResourceEntity); cq.select(from.get("id").as(Long.class)); List<Predicate> codePredicates = new ArrayList<Predicate>(); for (IQueryParameterType nextOr : theList) { IQueryParameterType params = nextOr; if (params instanceof NumberParam) { NumberParam param = (NumberParam) params; BigDecimal value = param.getValue(); if (value == null) { return thePids; } // Predicate p = predicateBuilder.translatePredicateValueNumber(myResourceEntity, builder, from, theParamName, param.getComparator() , value); Predicate p = predicateBuilder.translatePredicateValueNumber(myResourceEntity, builder, from, theParamName, param.getPrefix() , value); codePredicates.add(p); } else { throw new IllegalArgumentException("Invalid token type: " + params.getClass()); } } Predicate masterCodePredicate = builder.or(codePredicates.toArray(new Predicate[0])); if (thePids.size() > 0) { Predicate inPids = (from.get("id").in(thePids)); cq.where(builder.and( masterCodePredicate, inPids)); } else { cq.where(builder.and( masterCodePredicate)); } TypedQuery<Long> q = myEntityManager.createQuery(cq); return new HashSet<Long>(q.getResultList()); } public Set<Long> searchByQuantity(String theParamName, Set<Long> thePids, List<? extends IQueryParameterType> theList) { if (theList == null || theList.isEmpty()) { return thePids; } CriteriaBuilder builder = myEntityManager.getCriteriaBuilder(); CriteriaQuery<Long> cq = builder.createQuery(Long.class); Root<? extends IResourceEntity> from = cq.from(myResourceEntity); cq.select(from.get("id").as(Long.class)); List<Predicate> codePredicates = new ArrayList<Predicate>(); for (IQueryParameterType nextOr : theList) { IQueryParameterType params = nextOr; String systemValue; String unitsValue; ParamPrefixEnum cmpValue; BigDecimal valueValue; boolean approx = false; if (params instanceof BaseQuantityDt) { BaseQuantityDt param = (BaseQuantityDt) params; systemValue = param.getSystemElement().getValueAsString(); unitsValue = param.getUnitsElement().getValueAsString(); // cmpValue = QuantityCompararatorEnum.VALUESET_BINDER.fromCodeString(param.getComparatorElement().getValueAsString()); cmpValue = ParamPrefixEnum.forValue(param.getComparatorElement().getValueAsString()); valueValue = param.getValueElement().getValue(); } else if (params instanceof QuantityParam) { QuantityParam param = (QuantityParam) params; systemValue = param.getSystem(); // systemValue = param.getSystem().getValueAsString(); unitsValue = param.getUnits(); // cmpValue = param.getComparator(); cmpValue = param.getPrefix(); // valueValue = param.getValue().getValue(); valueValue = param.getValue(); approx = (param.getPrefix() == ParamPrefixEnum.APPROXIMATE); // approx = param.isApproximate(); } else { throw new IllegalArgumentException("Invalid quantity type: " + params.getClass()); } Predicate system = null; if (!isBlank(systemValue)) { system = predicateBuilder.translatePredicateQuantitySystem(myResourceEntity, theParamName, builder, from, systemValue); } Predicate code = null; if (!isBlank(unitsValue)) { code = predicateBuilder.translatePredicateQuantityCode(myResourceEntity, theParamName, builder, from, unitsValue); } Predicate num = predicateBuilder.translatePredicateQuantityValue(myResourceEntity, theParamName, builder, from, cmpValue, valueValue, approx); if (system == null && code == null) { codePredicates.add(num); } else if (system == null) { Predicate singleCode = builder.and(code, num); codePredicates.add(singleCode); } else if (code == null) { Predicate singleCode = builder.and(system, num); codePredicates.add(singleCode); } else { Predicate singleCode = builder.and(system, code, num); codePredicates.add(singleCode); } } Predicate masterCodePredicate = builder.or(codePredicates.toArray(new Predicate[0])); if (thePids.size() > 0) { Predicate inPids = (from.get("id").in(thePids)); cq.where(builder.and(masterCodePredicate, inPids)); } else { cq.where(builder.and(masterCodePredicate)); } TypedQuery<Long> q = myEntityManager.createQuery(cq); return new HashSet<Long>(q.getResultList()); } protected IQueryParameterType toParameterType(RuntimeSearchParam theParam) { IQueryParameterType qp; switch (theParam.getParamType()) { case DATE: qp = new DateParam(); break; case NUMBER: qp = new NumberParam(); break; case QUANTITY: qp = new QuantityParam(); break; case STRING: qp = new StringParam(); break; case TOKEN: qp = new TokenParam(); break; case COMPOSITE: List<RuntimeSearchParam> compositeOf = theParam.getCompositeOf(); if (compositeOf.size() != 2) { throw new InternalErrorException("Parameter " + theParam.getName() + " has " + compositeOf.size() + " composite parts. Don't know how handlt this."); } IQueryParameterType leftParam = toParameterType(compositeOf.get(0)); IQueryParameterType rightParam = toParameterType(compositeOf.get(1)); qp = new CompositeParam<IQueryParameterType, IQueryParameterType>(leftParam, rightParam); break; case REFERENCE: qp = new ReferenceParam(); break; default: throw new InternalErrorException("Don't know how to convert param type: " + theParam.getParamType()); } return qp; } protected IQueryParameterType toParameterType(RuntimeSearchParam theParam, String theValueAsQueryToken) { IQueryParameterType qp = toParameterType(theParam); qp.setValueAsQueryToken(null, theValueAsQueryToken); return qp; } public EntityManager getMyEntityManager() { return myEntityManager; } public void setMyEntityManager(EntityManager myEntityManager) { this.myEntityManager = myEntityManager; } public Class<? extends IResourceEntity> getMyResourceEntity() { return myResourceEntity; } public void setMyResourceEntity( Class<? extends IResourceEntity> myResourceEntity) { this.myResourceEntity = myResourceEntity; } public Class<? extends IResource> getMyResourceType() { return myResourceType; } public void setMyResourceType(Class<? extends IResource> myResourceType) { this.myResourceType = myResourceType; } public FhirContext getMyContext() { return myContext; } public void setMyContext(FhirContext myContext) { this.myContext = myContext; } public IFhirDao getBaseFhirDao() { return baseFhirDao; } public void setBaseFhirDao(IFhirDao baseFhirDao) { this.baseFhirDao = baseFhirDao; } }