001/*-
002 * #%L
003 * HAPI FHIR JPA Server
004 * %%
005 * Copyright (C) 2014 - 2025 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.sp;
021
022import ca.uhn.fhir.jpa.api.config.JpaStorageSettings;
023import ca.uhn.fhir.jpa.dao.data.ISearchParamPresentDao;
024import ca.uhn.fhir.jpa.model.entity.ResourceTable;
025import ca.uhn.fhir.jpa.model.entity.SearchParamPresentEntity;
026import ca.uhn.fhir.jpa.util.AddRemoveCount;
027import com.google.common.annotations.VisibleForTesting;
028import org.springframework.beans.factory.annotation.Autowired;
029import org.springframework.stereotype.Service;
030
031import java.util.ArrayList;
032import java.util.Collection;
033import java.util.HashMap;
034import java.util.List;
035import java.util.Map;
036import java.util.Map.Entry;
037
038@Service
039public class SearchParamPresenceSvcImpl implements ISearchParamPresenceSvc {
040
041        @Autowired
042        private ISearchParamPresentDao mySearchParamPresentDao;
043
044        @Autowired
045        private JpaStorageSettings myStorageSettings;
046
047        @VisibleForTesting
048        public void setStorageSettings(JpaStorageSettings theStorageSettings) {
049                myStorageSettings = theStorageSettings;
050        }
051
052        @Override
053        public AddRemoveCount updatePresence(
054                        ResourceTable theResource, Collection<SearchParamPresentEntity> thePresenceEntities) {
055
056                thePresenceEntities.forEach(t -> t.setResource(theResource));
057                thePresenceEntities.forEach(t -> t.calculateHashes());
058
059                AddRemoveCount retVal = new AddRemoveCount();
060                if (myStorageSettings.getIndexMissingFields() == JpaStorageSettings.IndexEnabledEnum.DISABLED) {
061                        return retVal;
062                }
063
064                // Find existing entries
065                Collection<SearchParamPresentEntity> existing = theResource.getSearchParamPresents();
066                Map<Long, SearchParamPresentEntity> existingHashToPresence = new HashMap<>();
067                for (SearchParamPresentEntity nextExistingEntity : existing) {
068                        existingHashToPresence.put(nextExistingEntity.getHashPresence(), nextExistingEntity);
069                }
070
071                // Find newly wanted set of entries
072                Map<Long, SearchParamPresentEntity> newHashToPresence = new HashMap<>();
073                for (SearchParamPresentEntity next : thePresenceEntities) {
074                        newHashToPresence.put(next.getHashPresence(), next);
075                }
076
077                // Delete any that should be deleted
078                List<SearchParamPresentEntity> toDelete = new ArrayList<>();
079                for (Entry<Long, SearchParamPresentEntity> nextEntry : existingHashToPresence.entrySet()) {
080                        if (newHashToPresence.containsKey(nextEntry.getKey()) == false) {
081                                toDelete.add(nextEntry.getValue());
082                        }
083                }
084                // Add any that should be added
085                List<SearchParamPresentEntity> toAdd = new ArrayList<>();
086                for (Entry<Long, SearchParamPresentEntity> nextEntry : newHashToPresence.entrySet()) {
087                        if (existingHashToPresence.containsKey(nextEntry.getKey()) == false) {
088                                toAdd.add(nextEntry.getValue());
089                        }
090                }
091
092                // Try to reuse any entities we can
093                while (toDelete.size() > 0 && toAdd.size() > 0) {
094                        SearchParamPresentEntity nextToDelete = toDelete.remove(toDelete.size() - 1);
095                        SearchParamPresentEntity nextToAdd = toAdd.remove(toAdd.size() - 1);
096                        nextToDelete.updateValues(nextToAdd);
097                        mySearchParamPresentDao.save(nextToDelete);
098                        retVal.addToAddCount(1);
099                        retVal.addToRemoveCount(1);
100                }
101
102                mySearchParamPresentDao.deleteAll(toDelete);
103                retVal.addToRemoveCount(toDelete.size());
104
105                mySearchParamPresentDao.saveAll(toAdd);
106                retVal.addToRemoveCount(toAdd.size());
107
108                return retVal;
109        }
110}