001package ca.uhn.fhir.jpa.dao.predicate;
002
003/*-
004 * #%L
005 * HAPI FHIR JPA Server
006 * %%
007 * Copyright (C) 2014 - 2021 Smile CDR, Inc.
008 * %%
009 * Licensed under the Apache License, Version 2.0 (the "License");
010 * you may not use this file except in compliance with the License.
011 * You may obtain a copy of the License at
012 *
013 *      http://www.apache.org/licenses/LICENSE-2.0
014 *
015 * Unless required by applicable law or agreed to in writing, software
016 * distributed under the License is distributed on an "AS IS" BASIS,
017 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
018 * See the License for the specific language governing permissions and
019 * limitations under the License.
020 * #L%
021 */
022
023import ca.uhn.fhir.context.RuntimeSearchParam;
024import ca.uhn.fhir.interceptor.model.RequestPartitionId;
025import ca.uhn.fhir.jpa.dao.LegacySearchBuilder;
026import ca.uhn.fhir.jpa.model.entity.BaseResourceIndexedSearchParam;
027import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamQuantity;
028import ca.uhn.fhir.model.api.IQueryParameterType;
029import ca.uhn.fhir.model.base.composite.BaseQuantityDt;
030import ca.uhn.fhir.rest.param.ParamPrefixEnum;
031import ca.uhn.fhir.rest.param.QuantityParam;
032import org.springframework.context.annotation.Scope;
033import org.springframework.stereotype.Component;
034
035import javax.persistence.criteria.CriteriaBuilder;
036import javax.persistence.criteria.Expression;
037import javax.persistence.criteria.From;
038import javax.persistence.criteria.Predicate;
039import java.math.BigDecimal;
040import java.util.ArrayList;
041import java.util.List;
042
043import static org.apache.commons.lang3.ObjectUtils.defaultIfNull;
044import static org.apache.commons.lang3.StringUtils.isBlank;
045
046@Component
047@Scope("prototype")
048public class PredicateBuilderQuantity extends BasePredicateBuilder implements IPredicateBuilder {
049
050        public PredicateBuilderQuantity(LegacySearchBuilder theSearchBuilder) {
051                super(theSearchBuilder);
052        }
053
054        @Override
055        public Predicate addPredicate(String theResourceName,
056                                                                                        RuntimeSearchParam theSearchParam,
057                                                                                        List<? extends IQueryParameterType> theList,
058                                                                                        SearchFilterParser.CompareOperation theOperation,
059                                                                                        RequestPartitionId theRequestPartitionId) {
060
061                From<?, ResourceIndexedSearchParamQuantity> join = myQueryStack.createJoin(SearchBuilderJoinEnum.QUANTITY, theSearchParam.getName());
062
063                if (theList.get(0).getMissing() != null) {
064                        addPredicateParamMissingForNonReference(theResourceName, theSearchParam.getName(), theList.get(0).getMissing(), join, theRequestPartitionId);
065                        return null;
066                }
067
068                List<Predicate> codePredicates = new ArrayList<>();
069                addPartitionIdPredicate(theRequestPartitionId, join, codePredicates);
070
071                for (IQueryParameterType nextOr : theList) {
072                        Predicate singleCode = createPredicateQuantity(nextOr, theResourceName, theSearchParam.getName(), myCriteriaBuilder, join, theOperation, theRequestPartitionId);
073                        codePredicates.add(singleCode);
074                }
075
076                Predicate retVal = myCriteriaBuilder.or(toArray(codePredicates));
077                myQueryStack.addPredicateWithImplicitTypeSelection(retVal);
078                return retVal;
079        }
080
081        public Predicate createPredicateQuantity(IQueryParameterType theParam,
082                                                                                                                  String theResourceName,
083                                                                                                                  String theParamName,
084                                                                                                                  CriteriaBuilder theBuilder,
085                                                                                                                  From<?, ResourceIndexedSearchParamQuantity> theFrom,
086                                                                                                                  RequestPartitionId theRequestPartitionId) {
087                return createPredicateQuantity(theParam,
088                        theResourceName,
089                        theParamName,
090                        theBuilder,
091                        theFrom,
092                        null,
093                theRequestPartitionId);
094        }
095
096        private Predicate createPredicateQuantity(IQueryParameterType theParam,
097                                                                                                                        String theResourceName,
098                                                                                                                        String theParamName,
099                                                                                                                        CriteriaBuilder theBuilder,
100                                                                                                                        From<?, ResourceIndexedSearchParamQuantity> theFrom,
101                                                                                                                        SearchFilterParser.CompareOperation operation,
102                                                                                                                        RequestPartitionId theRequestPartitionId) {
103                String systemValue;
104                String unitsValue;
105                ParamPrefixEnum cmpValue = null;
106                BigDecimal valueValue;
107
108                if (operation == SearchFilterParser.CompareOperation.ne) {
109                        cmpValue = ParamPrefixEnum.NOT_EQUAL;
110                } else if (operation == SearchFilterParser.CompareOperation.lt) {
111                        cmpValue = ParamPrefixEnum.LESSTHAN;
112                } else if (operation == SearchFilterParser.CompareOperation.le) {
113                        cmpValue = ParamPrefixEnum.LESSTHAN_OR_EQUALS;
114                } else if (operation == SearchFilterParser.CompareOperation.gt) {
115                        cmpValue = ParamPrefixEnum.GREATERTHAN;
116                } else if (operation == SearchFilterParser.CompareOperation.ge) {
117                        cmpValue = ParamPrefixEnum.GREATERTHAN_OR_EQUALS;
118                } else if (operation == SearchFilterParser.CompareOperation.eq) {
119                        cmpValue = ParamPrefixEnum.EQUAL;
120                } else if (operation != null) {
121                        throw new IllegalArgumentException("Invalid operator specified for quantity type");
122                }
123
124                if (theParam instanceof BaseQuantityDt) {
125                        BaseQuantityDt param = (BaseQuantityDt) theParam;
126                        systemValue = param.getSystemElement().getValueAsString();
127                        unitsValue = param.getUnitsElement().getValueAsString();
128                        if (operation == null) {
129                                cmpValue = ParamPrefixEnum.forValue(param.getComparatorElement().getValueAsString());
130                        }
131                        valueValue = param.getValueElement().getValue();
132                } else if (theParam instanceof QuantityParam) {
133                        QuantityParam param = (QuantityParam) theParam;
134                        systemValue = param.getSystem();
135                        unitsValue = param.getUnits();
136                        if (operation == null) {
137                                cmpValue = param.getPrefix();
138                        }
139                        valueValue = param.getValue();
140                } else {
141                        throw new IllegalArgumentException("Invalid quantity type: " + theParam.getClass());
142                }
143
144                if (myDontUseHashesForSearch) {
145                        Predicate system = null;
146                        if (!isBlank(systemValue)) {
147                                system = theBuilder.equal(theFrom.get("mySystem"), systemValue);
148                        }
149
150                        Predicate code = null;
151                        if (!isBlank(unitsValue)) {
152                                code = theBuilder.equal(theFrom.get("myUnits"), unitsValue);
153                        }
154
155                        cmpValue = defaultIfNull(cmpValue, ParamPrefixEnum.EQUAL);
156                        final Expression<BigDecimal> path = theFrom.get("myValue");
157                        String invalidMessageName = "invalidQuantityPrefix";
158
159                        Predicate num = createPredicateNumeric(theResourceName, null, theFrom, theBuilder, theParam, cmpValue, valueValue, path, invalidMessageName, theRequestPartitionId);
160
161                        Predicate singleCode;
162                        if (system == null && code == null) {
163                                singleCode = num;
164                        } else if (system == null) {
165                                singleCode = theBuilder.and(code, num);
166                        } else if (code == null) {
167                                singleCode = theBuilder.and(system, num);
168                        } else {
169                                singleCode = theBuilder.and(system, code, num);
170                        }
171
172                        return combineParamIndexPredicateWithParamNamePredicate(theResourceName, theParamName, theFrom, singleCode, theRequestPartitionId);
173                }
174
175                Predicate hashPredicate;
176                if (!isBlank(systemValue) && !isBlank(unitsValue)) {
177                        long hash = ResourceIndexedSearchParamQuantity.calculateHashSystemAndUnits(getPartitionSettings(), theRequestPartitionId, theResourceName, theParamName, systemValue, unitsValue);
178                        hashPredicate = myCriteriaBuilder.equal(theFrom.get("myHashIdentitySystemAndUnits"), hash);
179                } else if (!isBlank(unitsValue)) {
180                        long hash = ResourceIndexedSearchParamQuantity.calculateHashUnits(getPartitionSettings(), theRequestPartitionId, theResourceName, theParamName, unitsValue);
181                        hashPredicate = myCriteriaBuilder.equal(theFrom.get("myHashIdentityAndUnits"), hash);
182                } else {
183                        long hash = BaseResourceIndexedSearchParam.calculateHashIdentity(getPartitionSettings(), theRequestPartitionId, theResourceName, theParamName);
184                        hashPredicate = myCriteriaBuilder.equal(theFrom.get("myHashIdentity"), hash);
185                }
186
187                cmpValue = defaultIfNull(cmpValue, ParamPrefixEnum.EQUAL);
188                final Expression<BigDecimal> path = theFrom.get("myValue");
189                String invalidMessageName = "invalidQuantityPrefix";
190
191                Predicate numericPredicate = createPredicateNumeric(theResourceName, null, theFrom, theBuilder, theParam, cmpValue, valueValue, path, invalidMessageName, theRequestPartitionId);
192
193                return theBuilder.and(hashPredicate, numericPredicate);
194        }
195
196
197}