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        DbColumn getPartitionIdColumn() {
055                return myColumnPartitionId;
056        }
057
058        public Condition combineWithRequestPartitionIdPredicate(
059                        RequestPartitionId theRequestPartitionId, Condition theCondition) {
060                Condition partitionIdPredicate = createPartitionIdPredicate(theRequestPartitionId);
061                if (partitionIdPredicate == null) {
062                        return theCondition;
063                }
064                return QueryParameterUtils.toAndPredicate(partitionIdPredicate, theCondition);
065        }
066
067        @Nullable
068        public Condition createPartitionIdPredicate(RequestPartitionId theRequestPartitionId) {
069
070                if (theRequestPartitionId != null && !theRequestPartitionId.isAllPartitions()) {
071                        Condition condition;
072
073                        boolean defaultPartitionIsNull = getPartitionSettings().getDefaultPartitionId() == null;
074                        if (theRequestPartitionId.isDefaultPartition() && defaultPartitionIsNull) {
075                                condition = UnaryCondition.isNull(getPartitionIdColumn());
076                        } else if (theRequestPartitionId.hasDefaultPartitionId() && defaultPartitionIsNull) {
077                                List<String> placeholders = generatePlaceholders(theRequestPartitionId.getPartitionIdsWithoutDefault());
078                                UnaryCondition partitionNullPredicate = UnaryCondition.isNull(getPartitionIdColumn());
079                                Condition partitionIdsPredicate =
080                                                QueryParameterUtils.toEqualToOrInPredicate(getPartitionIdColumn(), placeholders);
081                                condition = QueryParameterUtils.toOrPredicate(partitionNullPredicate, partitionIdsPredicate);
082                        } else {
083                                List<Integer> partitionIds = theRequestPartitionId.getPartitionIds();
084                                partitionIds = replaceDefaultPartitionIdIfNonNull(getPartitionSettings(), partitionIds);
085
086                                List<String> placeholders = generatePlaceholders(partitionIds);
087                                condition = QueryParameterUtils.toEqualToOrInPredicate(getPartitionIdColumn(), placeholders);
088                        }
089                        return condition;
090                } else {
091                        return null;
092                }
093        }
094
095        public Condition createPredicateResourceIds(boolean theInverse, List<Long> theResourceIds) {
096                Validate.notNull(theResourceIds, "theResourceIds must not be null");
097
098                // Handle the _id parameter by adding it to the tail
099                Condition inResourceIds =
100                                QueryParameterUtils.toEqualToOrInPredicate(getResourceIdColumn(), generatePlaceholders(theResourceIds));
101                if (theInverse) {
102                        inResourceIds = new NotCondition(inResourceIds);
103                }
104                return inResourceIds;
105        }
106
107        public static List<Integer> replaceDefaultPartitionIdIfNonNull(
108                        PartitionSettings thePartitionSettings, List<Integer> thePartitionIds) {
109                List<Integer> partitionIds = thePartitionIds;
110                if (thePartitionSettings.getDefaultPartitionId() != null) {
111                        partitionIds = partitionIds.stream()
112                                        .map(t -> t == null ? thePartitionSettings.getDefaultPartitionId() : t)
113                                        .collect(Collectors.toList());
114                }
115                return partitionIds;
116        }
117}