001/*-
002 * #%L
003 * HAPI FHIR JPA Server
004 * %%
005 * Copyright (C) 2014 - 2024 Smile CDR, Inc.
006 * %%
007 * Licensed under the Apache License, Version 2.0 (the "License");
008 * you may not use this file except in compliance with the License.
009 * You may obtain a copy of the License at
010 *
011 *      http://www.apache.org/licenses/LICENSE-2.0
012 *
013 * Unless required by applicable law or agreed to in writing, software
014 * distributed under the License is distributed on an "AS IS" BASIS,
015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016 * See the License for the specific language governing permissions and
017 * limitations under the License.
018 * #L%
019 */
020package ca.uhn.fhir.jpa.search.builder.predicate;
021
022import ca.uhn.fhir.interceptor.model.RequestPartitionId;
023import ca.uhn.fhir.jpa.model.config.PartitionSettings;
024import ca.uhn.fhir.jpa.search.builder.sql.SearchQueryBuilder;
025import ca.uhn.fhir.jpa.util.QueryParameterUtils;
026import com.healthmarketscience.sqlbuilder.Condition;
027import com.healthmarketscience.sqlbuilder.NotCondition;
028import com.healthmarketscience.sqlbuilder.UnaryCondition;
029import com.healthmarketscience.sqlbuilder.dbspec.basic.DbColumn;
030import com.healthmarketscience.sqlbuilder.dbspec.basic.DbTable;
031import jakarta.annotation.Nullable;
032import org.apache.commons.lang3.Validate;
033
034import java.util.List;
035import java.util.stream.Collectors;
036
037public abstract class BaseJoiningPredicateBuilder extends BasePredicateBuilder {
038
039        private final DbTable myTable;
040        private final DbColumn myColumnPartitionId;
041
042        BaseJoiningPredicateBuilder(SearchQueryBuilder theSearchSqlBuilder, DbTable theTable) {
043                super(theSearchSqlBuilder);
044                myTable = theTable;
045                myColumnPartitionId = theTable.addColumn("PARTITION_ID");
046        }
047
048        public DbTable getTable() {
049                return myTable;
050        }
051
052        public abstract DbColumn getResourceIdColumn();
053
054        public DbColumn getPartitionIdColumn() {
055                return myColumnPartitionId;
056        }
057
058        public DbColumn[] getJoinColumns() {
059                return getSearchQueryBuilder().toJoinColumns(getPartitionIdColumn(), getResourceIdColumn());
060        }
061
062        public Condition combineWithRequestPartitionIdPredicate(
063                        RequestPartitionId theRequestPartitionId, Condition theCondition) {
064                Condition partitionIdPredicate = createPartitionIdPredicate(theRequestPartitionId);
065                if (partitionIdPredicate == null) {
066                        return theCondition;
067                }
068                return QueryParameterUtils.toAndPredicate(partitionIdPredicate, theCondition);
069        }
070
071        @Nullable
072        public Condition createPartitionIdPredicate(RequestPartitionId theRequestPartitionId) {
073
074                if (theRequestPartitionId != null && !theRequestPartitionId.isAllPartitions()) {
075                        Condition condition;
076
077                        boolean defaultPartitionIsNull = getPartitionSettings().getDefaultPartitionId() == null;
078                        if (theRequestPartitionId.isDefaultPartition() && defaultPartitionIsNull) {
079                                condition = UnaryCondition.isNull(getPartitionIdColumn());
080                        } else if (theRequestPartitionId.hasDefaultPartitionId() && defaultPartitionIsNull) {
081                                List<String> placeholders = generatePlaceholders(theRequestPartitionId.getPartitionIdsWithoutDefault());
082                                UnaryCondition partitionNullPredicate = UnaryCondition.isNull(getPartitionIdColumn());
083                                Condition partitionIdsPredicate =
084                                                QueryParameterUtils.toEqualToOrInPredicate(getPartitionIdColumn(), placeholders);
085                                condition = QueryParameterUtils.toOrPredicate(partitionNullPredicate, partitionIdsPredicate);
086                        } else {
087                                List<Integer> partitionIds = theRequestPartitionId.getPartitionIds();
088                                partitionIds = replaceDefaultPartitionIdIfNonNull(getPartitionSettings(), partitionIds);
089
090                                List<String> placeholders = generatePlaceholders(partitionIds);
091                                condition = QueryParameterUtils.toEqualToOrInPredicate(getPartitionIdColumn(), placeholders);
092                        }
093                        return condition;
094                } else {
095                        return null;
096                }
097        }
098
099        public Condition createPredicateResourceIds(boolean theInverse, List<Long> theResourceIds) {
100                Validate.notNull(theResourceIds, "theResourceIds must not be null");
101
102                // Handle the _id parameter by adding it to the tail
103                Condition inResourceIds =
104                                QueryParameterUtils.toEqualToOrInPredicate(getResourceIdColumn(), generatePlaceholders(theResourceIds));
105                if (theInverse) {
106                        inResourceIds = new NotCondition(inResourceIds);
107                }
108                return inResourceIds;
109        }
110
111        public static List<Integer> replaceDefaultPartitionIdIfNonNull(
112                        PartitionSettings thePartitionSettings, List<Integer> thePartitionIds) {
113                List<Integer> partitionIds = thePartitionIds;
114                if (thePartitionSettings.getDefaultPartitionId() != null) {
115                        partitionIds = partitionIds.stream()
116                                        .map(t -> t == null ? thePartitionSettings.getDefaultPartitionId() : t)
117                                        .collect(Collectors.toList());
118                }
119                return partitionIds;
120        }
121}