001/*-
002 * #%L
003 * HAPI FHIR JPA Model
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.model.entity;
021
022import ca.uhn.fhir.context.ParserOptions;
023import ca.uhn.fhir.i18n.Msg;
024import ca.uhn.fhir.jpa.model.config.PartitionSettings;
025import ca.uhn.fhir.jpa.util.ISequenceValueMassager;
026import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
027import ca.uhn.fhir.rest.server.interceptor.ResponseTerminologyTranslationSvc;
028import ca.uhn.fhir.rest.server.util.ISearchParamRegistry;
029import ca.uhn.fhir.util.HapiExtensions;
030import jakarta.annotation.Nonnull;
031import org.apache.commons.lang3.Validate;
032import org.hl7.fhir.instance.model.api.IPrimitiveType;
033import org.hl7.fhir.r4.model.DateTimeType;
034
035import java.util.Arrays;
036import java.util.Collections;
037import java.util.Date;
038import java.util.HashMap;
039import java.util.HashSet;
040import java.util.Map;
041import java.util.Set;
042
043import static org.apache.commons.lang3.ObjectUtils.defaultIfNull;
044
045/**
046 * This class contains configuration options common to all hapi-fhir-storage implementations.
047 * Ultimately it should live in that project
048 */
049public class StorageSettings {
050        /**
051         * @since 5.6.0
052         */
053        // Thread Pool size used by batch in bundle
054        public static final int DEFAULT_BUNDLE_BATCH_POOL_SIZE = 20; // 1 for single thread
055
056        public static final int DEFAULT_BUNDLE_BATCH_MAX_POOL_SIZE = 100; // 1 for single thread
057        /**
058         * Default {@link #getTreatReferencesAsLogical() logical URL bases}. Includes the following
059         * values:
060         * <ul>
061         * <li><code>"http://hl7.org/fhir/valueset-*"</code></li>
062         * <li><code>"http://hl7.org/fhir/codesystem-*"</code></li>
063         * <li><code>"http://hl7.org/fhir/StructureDefinition/*"</code></li>
064         * </ul>
065         */
066        public static final Set<String> DEFAULT_LOGICAL_BASE_URLS = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(
067                        "http://hl7.org/fhir/ValueSet/*",
068                        "http://hl7.org/fhir/CodeSystem/*",
069                        "http://hl7.org/fhir/valueset-*",
070                        "http://hl7.org/fhir/codesystem-*",
071                        "http://hl7.org/fhir/StructureDefinition/*")));
072
073        /*
074         * <p>
075         * Note the following database documented limitations:
076         *    <ul>
077         *       <li>JDBC Timestamp Datatype Low Value -4713 and High Value 9999</li>
078         *       <li>MySQL 8: the range for DATETIME values is '1000-01-01 00:00:00.000000' to '9999-12-31 23:59:59.999999`</li>
079         *       <li>Postgresql 12: Timestamp [without time zone] Low Value 4713 BC and High Value 294276 AD</li>
080         *       <li>Oracle: Timestamp Low Value 4712 BC and High Value 9999 CE</li>
081         *       <li>H2: datetime2 Low Value -4713 and High Value 9999</li>
082         *     </ul>
083         * </p>
084         */
085        protected static final String DEFAULT_PERIOD_INDEX_START_OF_TIME = "1001-01-01";
086        protected static final String DEFAULT_PERIOD_INDEX_END_OF_TIME = "9000-01-01";
087        private static final Integer DEFAULT_MAXIMUM_TRANSACTION_BUNDLE_SIZE = null;
088        /**
089         * @since 5.5.0
090         */
091        public static final TagStorageModeEnum DEFAULT_TAG_STORAGE_MODE = TagStorageModeEnum.VERSIONED;
092
093        /**
094         * update setter javadoc if default changes
095         */
096        private boolean myAllowContainsSearches = false;
097
098        private boolean myAllowExternalReferences = false;
099        private Set<String> myTreatBaseUrlsAsLocal = new HashSet<>();
100        private Set<String> myTreatReferencesAsLogical = new HashSet<>(DEFAULT_LOGICAL_BASE_URLS);
101        private boolean myDefaultSearchParamsCanBeOverridden = true;
102        private boolean myAutoCreatePlaceholderReferenceTargets;
103        private Integer myBundleBatchPoolSize = DEFAULT_BUNDLE_BATCH_POOL_SIZE;
104        private Integer myBundleBatchMaxPoolSize = DEFAULT_BUNDLE_BATCH_MAX_POOL_SIZE;
105        private boolean myMassIngestionMode;
106        private Integer myMaximumTransactionBundleSize = DEFAULT_MAXIMUM_TRANSACTION_BUNDLE_SIZE;
107        private TagStorageModeEnum myTagStorageMode = DEFAULT_TAG_STORAGE_MODE;
108        /**
109         * Activates hibernate search indexing of fulltext data from resources, which
110         * is used to support the {@literal _text} and {@literal _content} Search Parameters.
111         *
112         * @since 8.0.0
113         */
114        private boolean myHibernateSearchIndexFullText = false;
115
116        private boolean myNormalizeTerminologyForBulkExportJobs = false;
117        /**
118         * Update setter javadoc if default changes.
119         */
120        private boolean myUseOrdinalDatesForDayPrecisionSearches = true;
121
122        private boolean mySuppressStringIndexingInTokens = false;
123        private Class<? extends ISequenceValueMassager> mySequenceValueMassagerClass;
124        private IPrimitiveType<Date> myPeriodIndexStartOfTime;
125        private IPrimitiveType<Date> myPeriodIndexEndOfTime;
126        private NormalizedQuantitySearchLevel myNormalizedQuantitySearchLevel;
127        private Set<String> myAutoVersionReferenceAtPaths = Collections.emptySet();
128        private Map<String, Set<String>> myTypeToAutoVersionReferenceAtPaths = Collections.emptyMap();
129        private boolean myRespectVersionsForSearchIncludes;
130        private boolean myIndexOnUpliftedRefchains = false;
131        private boolean myIndexOnContainedResources = false;
132        private boolean myIndexOnContainedResourcesRecursively = false;
133        private boolean myAllowMdmExpansion = false;
134        private boolean myAutoSupportDefaultSearchParams = true;
135        private boolean myIndexIdentifierOfType = false;
136        private IndexEnabledEnum myIndexMissingFieldsEnabled = IndexEnabledEnum.DISABLED;
137
138        /**
139         * Should the {@literal _lamguage} SearchParameter be supported
140         * on this server?
141         *
142         * @since 7.0.0
143         */
144        private boolean myLanguageSearchParameterEnabled = false;
145
146        /**
147         * If set to false, all resource types will be installed via package installer, regardless of their status.
148         * Otherwise, resources will be filtered based on status according to some criteria which can be found in
149         * <code>PackageInstallerSvcImpl#isValidResourceStatusForPackageUpload<code>
150         * @since 7.0.0
151         */
152        private boolean myValidateResourceStatusForPackageUpload = true;
153
154        /**
155         * If set to <code>true</code>, the server will not write data to the <code>SP_NAME, RES_TYPE</code>
156         * columns for all HFJ_SPIDX tables.
157         *
158         * @since 7.4.0
159         */
160        private boolean myIndexStorageOptimized = false;
161
162        /**
163         * Constructor
164         */
165        public StorageSettings() {
166                setSequenceValueMassagerClass(ISequenceValueMassager.NoopSequenceValueMassager.class);
167                setPeriodIndexStartOfTime(new DateTimeType(DEFAULT_PERIOD_INDEX_START_OF_TIME));
168                setPeriodIndexEndOfTime(new DateTimeType(DEFAULT_PERIOD_INDEX_END_OF_TIME));
169                setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_NOT_SUPPORTED);
170        }
171
172        /**
173         * When creating or updating a resource: If this property is set to <code>true</code>
174         * (default is <code>false</code>), if the resource has a reference to another resource
175         * on the local server but that reference does not exist, a placeholder resource will be
176         * created.
177         * <p>
178         * In other words, if an observation with subject <code>Patient/FOO</code> is created, but
179         * there is no resource called <code>Patient/FOO</code> on the server, this property causes
180         * an empty patient with ID "FOO" to be created in order to prevent this operation
181         * from failing.
182         * </p>
183         * <p>
184         * This property can be useful in cases where replication between two servers is wanted.
185         * Note however that references containing purely numeric IDs will not be auto-created
186         * as they are never allowed to be client supplied in HAPI FHIR JPA.
187         * <p>
188         * All placeholder resources created in this way have an extension
189         * with the URL {@link HapiExtensions#EXT_RESOURCE_PLACEHOLDER} and the value "true".
190         * </p>
191         */
192        public boolean isAutoCreatePlaceholderReferenceTargets() {
193                return myAutoCreatePlaceholderReferenceTargets;
194        }
195
196        /**
197         * When creating or updating a resource: If this property is set to <code>true</code>
198         * (default is <code>false</code>), if the resource has a reference to another resource
199         * on the local server but that reference does not exist, a placeholder resource will be
200         * created.
201         * <p>
202         * In other words, if an observation with subject <code>Patient/FOO</code> is created, but
203         * there is no resource called <code>Patient/FOO</code> on the server, this property causes
204         * an empty patient with ID "FOO" to be created in order to prevent this operation
205         * from failing.
206         * </p>
207         * <p>
208         * This property can be useful in cases where replication between two servers is wanted.
209         * Note however that references containing purely numeric IDs will not be auto-created
210         * as they are never allowed to be client supplied in HAPI FHIR JPA.
211         * <p>
212         * All placeholder resources created in this way have an extension
213         * with the URL {@link HapiExtensions#EXT_RESOURCE_PLACEHOLDER} and the value "true".
214         * </p>
215         */
216        public void setAutoCreatePlaceholderReferenceTargets(boolean theAutoCreatePlaceholderReferenceTargets) {
217                myAutoCreatePlaceholderReferenceTargets = theAutoCreatePlaceholderReferenceTargets;
218        }
219
220        /**
221         * Get the batch transaction thread pool size.
222         *
223         * @since 5.6.0
224         */
225        public Integer getBundleBatchPoolSize() {
226                return myBundleBatchPoolSize;
227        }
228
229        /**
230         * Set the batch transaction thread pool size. The default is @see {@link #DEFAULT_BUNDLE_BATCH_POOL_SIZE}
231         * set pool size to 1 for single thread
232         *
233         * @since 5.6.0
234         */
235        public void setBundleBatchPoolSize(Integer theBundleBatchPoolSize) {
236                this.myBundleBatchPoolSize = theBundleBatchPoolSize;
237        }
238
239        /**
240         * Get the batch transaction thread max pool size.
241         * set max pool size to 1 for single thread
242         *
243         * @since 5.6.0
244         */
245        public Integer getBundleBatchMaxPoolSize() {
246                return myBundleBatchMaxPoolSize;
247        }
248
249        /**
250         * Set the batch transaction thread pool size. The default is @see {@link #DEFAULT_BUNDLE_BATCH_MAX_POOL_SIZE}
251         *
252         * @since 5.6.0
253         */
254        public void setBundleBatchMaxPoolSize(Integer theBundleBatchMaxPoolSize) {
255                this.myBundleBatchMaxPoolSize = theBundleBatchMaxPoolSize;
256        }
257
258        /**
259         * If set to {@link IndexEnabledEnum#DISABLED} (default is {@link IndexEnabledEnum#DISABLED})
260         * the server will not create search indexes for search parameters with no values in resources.
261         * <p>
262         * Disabling this feature means that the <code>:missing</code> search modifier will not be
263         * supported on the server, but also means that storage and indexing (i.e. writes to the
264         * database) may be much faster on servers which have lots of search parameters and need
265         * to write quickly.
266         * </p>
267         * <p>
268         * This feature may be enabled on servers where supporting the use of the :missing parameter is
269         * of higher importance than raw write performance
270         * </p>
271         */
272        public IndexEnabledEnum getIndexMissingFields() {
273                return myIndexMissingFieldsEnabled;
274        }
275
276        /**
277         * Is hibernate search indexing of fulltext data from resources enabled?
278         * This setting activates hibernate search indexing of fulltext data from resources, which
279         * is used to support the {@literal _text} and {@literal _content} Search Parameters.
280         *
281         * @since 8.0.0
282         */
283        public boolean isHibernateSearchIndexFullText() {
284                return myHibernateSearchIndexFullText;
285        }
286
287        /**
288         * Activates hibernate search indexing of fulltext data from resources, which
289         * is used to support the {@literal _text} and {@literal _content} Search Parameters.
290         * <p>
291         * Note that if you enable this setting at runtime (i.e. on an already running server), you
292         * may need to also invoke {@link ISearchParamRegistry#forceRefresh()} in order to ensure
293         * that the implicit search parameters needed for this feature get inserted into the registry.
294         * </p>
295         *
296         * @since 8.0.0
297         */
298        public void setHibernateSearchIndexFullText(boolean theHibernateSearchIndexFullText) {
299                myHibernateSearchIndexFullText = theHibernateSearchIndexFullText;
300        }
301
302        /**
303         * If set to {@link IndexEnabledEnum#DISABLED} (default is {@link IndexEnabledEnum#DISABLED})
304         * the server will not create search indexes for search parameters with no values in resources.
305         * <p>
306         * Disabling this feature means that the <code>:missing</code> search modifier will not be
307         * supported on the server, but also means that storage and indexing (i.e. writes to the
308         * database) may be much faster on servers which have lots of search parameters and need
309         * to write quickly.
310         * </p>
311         * <p>
312         * This feature may be enabled on servers where supporting the use of the :missing parameter is
313         * of higher importance than raw write performance
314         * </p>
315         * <p>
316         * Note that this setting also has an impact on sorting (i.e. using the
317         * <code>_sort</code> parameter on searches): If the server is configured
318         * to not index missing field.
319         * </p>
320         * <p>
321         * The following index may need to be added into the indexed tables such as <code>HFJ_SPIDX_TOKEN</code>
322         * to improve the search performance while <code>:missing</code> is enabled.
323         * <code>RES_TYPE, SP_NAME, SP_MISSING</code>
324         * </p>
325         */
326        public void setIndexMissingFields(IndexEnabledEnum theIndexMissingFields) {
327                Validate.notNull(theIndexMissingFields, "theIndexMissingFields must not be null");
328                myIndexMissingFieldsEnabled = theIndexMissingFields;
329        }
330
331        /**
332         * If set to <code>true</code> (default is false), the server will not write data
333         * to the <code>SP_NAME, RES_TYPE</code> columns for all HFJ_SPIDX tables.
334         * <p>
335         * This feature may be enabled on servers where HFJ_SPIDX tables are expected
336         * to have a large amount of data (millions of rows) in order to reduce overall storage size.
337         * </p>
338         * <p>
339         * Note that this setting only applies to newly inserted and updated rows in HFJ_SPIDX tables.
340         * In order to apply this optimization setting to existing HFJ_SPIDX index rows,
341         * <code>$reindex</code> operation should be executed at the instance or server level.
342         * <p>
343         * <p>
344         * If this setting is enabled, {@link PartitionSettings#isIncludePartitionInSearchHashes()} should be disabled.
345         * </p>
346         * <p>
347         * If {@link StorageSettings#getIndexMissingFields()} is enabled, the following index may need to be added
348         * into the HFJ_SPIDX tables to improve the search performance: <code>HASH_IDENTITY, SP_MISSING, RES_ID, PARTITION_ID</code>
349         * </p>
350         *
351         * @since 7.4.0
352         */
353        public boolean isIndexStorageOptimized() {
354                return myIndexStorageOptimized;
355        }
356
357        /**
358         * If set to <code>true</code> (default is false), the server will not write data
359         * to the <code>SP_NAME, RES_TYPE</code> columns for all HFJ_SPIDX tables.
360         * <p>
361         * This feature may be enabled on servers where HFJ_SPIDX tables are expected
362         * to have a large amount of data (millions of rows) in order to reduce overall storage size.
363         * </p>
364         * <p>
365         * Note that this setting only applies to newly inserted and updated rows in HFJ_SPIDX tables.
366         * In order to apply this optimization setting to existing HFJ_SPIDX index rows,
367         * <code>$reindex</code> operation should be executed at the instance or server level.
368         * <p>
369         * <p>
370         * If this setting is enabled, {@link PartitionSettings#isIncludePartitionInSearchHashes()} should be set to <code>false</code>.
371         * </p>
372         * <p>
373         * If {@link StorageSettings#getIndexMissingFields()} is enabled, the following index may need to be added
374         * into the HFJ_SPIDX tables to improve the search performance: <code>HASH_IDENTITY, SP_MISSING, RES_ID, PARTITION_ID</code>
375         * </p>
376         *
377         * @since 7.4.0
378         */
379        public void setIndexStorageOptimized(boolean theIndexStorageOptimized) {
380                myIndexStorageOptimized = theIndexStorageOptimized;
381        }
382
383        /**
384         * If this is enabled (disabled by default), Mass Ingestion Mode is enabled. In this mode, a number of
385         * runtime checks are disabled. This mode is designed for rapid backloading of data while the system is not
386         * being otherwise used.
387         * <p>
388         * In this mode:
389         * <p>
390         * - Tags/Profiles/Security Labels will not be updated on existing resources that already have them
391         * - Resources modification checks will be skipped in favour of a simple hash check
392         * - Extra resource ID caching is enabled
393         *
394         * @since 5.5.0
395         */
396        public boolean isMassIngestionMode() {
397                return myMassIngestionMode;
398        }
399
400        /**
401         * If this is enabled (disabled by default), Mass Ingestion Mode is enabled. In this mode, a number of
402         * runtime checks are disabled. This mode is designed for rapid backloading of data while the system is not
403         * being otherwise used.
404         * <p>
405         * In this mode:
406         * <p>
407         * - Tags/Profiles/Security Labels will not be updated on existing resources that already have them
408         * - Resources modification checks will be skipped in favour of a simple hash check
409         * - Extra resource ID caching is enabled
410         *
411         * @since 5.5.0
412         */
413        public void setMassIngestionMode(boolean theMassIngestionMode) {
414                myMassIngestionMode = theMassIngestionMode;
415        }
416
417        /**
418         * Specifies the maximum number of resources permitted within a single transaction bundle.
419         * If a transaction bundle is submitted with more than this number of resources, it will be
420         * rejected with a PayloadTooLarge exception.
421         * <p>
422         * The default value is <code>null</code>, which means that there is no limit.
423         * </p>
424         */
425        public Integer getMaximumTransactionBundleSize() {
426                return myMaximumTransactionBundleSize;
427        }
428
429        /**
430         * Specifies the maximum number of resources permitted within a single transaction bundle.
431         * If a transaction bundle is submitted with more than this number of resources, it will be
432         * rejected with a PayloadTooLarge exception.
433         * <p>
434         * The default value is <code>null</code>, which means that there is no limit.
435         * </p>
436         */
437        public StorageSettings setMaximumTransactionBundleSize(Integer theMaximumTransactionBundleSize) {
438                myMaximumTransactionBundleSize = theMaximumTransactionBundleSize;
439                return this;
440        }
441
442        /**
443         * If set to true, attempt to map terminology for bulk export jobs using the
444         * logic in
445         * {@link ResponseTerminologyTranslationSvc}. Default is <code>false</code>.
446         *
447         * @since 6.3.0
448         */
449        public boolean isNormalizeTerminologyForBulkExportJobs() {
450                return myNormalizeTerminologyForBulkExportJobs;
451        }
452
453        /**
454         * If set to true, attempt to map terminology for bulk export jobs using the
455         * logic in
456         * {@link ResponseTerminologyTranslationSvc}. Default is <code>false</code>.
457         *
458         * @since 6.3.0
459         */
460        public void setNormalizeTerminologyForBulkExportJobs(boolean theNormalizeTerminologyForBulkExportJobs) {
461                myNormalizeTerminologyForBulkExportJobs = theNormalizeTerminologyForBulkExportJobs;
462        }
463
464        /**
465         * This is an internal API and may change or disappear without notice
466         *
467         * @since 6.2.0
468         */
469        public Class<? extends ISequenceValueMassager> getSequenceValueMassagerClass() {
470                return mySequenceValueMassagerClass;
471        }
472
473        /**
474         * This is an internal API and may change or disappear without notice
475         *
476         * @since 6.2.0
477         */
478        public void setSequenceValueMassagerClass(Class<? extends ISequenceValueMassager> theSequenceValueMassagerClass) {
479                Validate.notNull(theSequenceValueMassagerClass, "theSequenceValueMassagerClass must not be null");
480                mySequenceValueMassagerClass = theSequenceValueMassagerClass;
481        }
482
483        /**
484         * Sets the tag storage mode for the server. Default is {@link TagStorageModeEnum#VERSIONED}.
485         *
486         * @since 5.5.0
487         */
488        @Nonnull
489        public TagStorageModeEnum getTagStorageMode() {
490                return myTagStorageMode;
491        }
492
493        /**
494         * Sets the tag storage mode for the server. Default is {@link TagStorageModeEnum#VERSIONED}.
495         *
496         * @since 5.5.0
497         */
498        public void setTagStorageMode(@Nonnull TagStorageModeEnum theTagStorageMode) {
499                Validate.notNull(theTagStorageMode, "theTagStorageMode must not be null");
500                myTagStorageMode = theTagStorageMode;
501        }
502
503        /**
504         * If set to <code>true</code> (default is <code>false</code>) the
505         * <code>:of-type</code> modifier on token search parameters for
506         * identifiers will be supported. Enabling this causes additional
507         * indexing overhead (although very minor) so it is disabled unless it is
508         * actually needed.
509         *
510         * @since 5.7.0
511         */
512        public boolean isIndexIdentifierOfType() {
513                return myIndexIdentifierOfType;
514        }
515
516        /**
517         * If set to <code>true</code> (default is <code>false</code>) the
518         * <code>:of-type</code> modifier on token search parameters for
519         * identifiers will be supported. Enabling this causes additional
520         * indexing overhead (although very minor) so it is disabled unless it is
521         * actually needed.
522         *
523         * @since 5.7.0
524         */
525        public void setIndexIdentifierOfType(boolean theIndexIdentifierOfType) {
526                myIndexIdentifierOfType = theIndexIdentifierOfType;
527        }
528
529        /**
530         * If set to {@code true} the default search params (i.e. the search parameters that are
531         * defined by the FHIR specification itself) may be overridden by uploading search
532         * parameters to the server with the same code as the built-in search parameter.
533         * <p>
534         * This can be useful if you want to be able to disable or alter
535         * the behaviour of the default search parameters.
536         * </p>
537         * <p>
538         * The default value for this setting is {@code true}
539         * </p>
540         */
541        public boolean isDefaultSearchParamsCanBeOverridden() {
542                return myDefaultSearchParamsCanBeOverridden;
543        }
544
545        /**
546         * If set to {@code true} the default search params (i.e. the search parameters that are
547         * defined by the FHIR specification itself) may be overridden by uploading search
548         * parameters to the server with the same code as the built-in search parameter.
549         * <p>
550         * This can be useful if you want to be able to disable or alter
551         * the behaviour of the default search parameters.
552         * </p>
553         * <p>
554         * The default value for this setting is {@code true}
555         * </p>
556         */
557        public void setDefaultSearchParamsCanBeOverridden(boolean theDefaultSearchParamsCanBeOverridden) {
558                myDefaultSearchParamsCanBeOverridden = theDefaultSearchParamsCanBeOverridden;
559        }
560
561        /**
562         * If enabled, the server will support the use of :contains searches,
563         * which are helpful but can have adverse effects on performance.
564         * <p>
565         * Default is <code>false</code> (Note that prior to HAPI FHIR
566         * 3.5.0 the default was <code>true</code>)
567         * </p>
568         * <p>
569         * Note: If you change this value after data already has
570         * already been stored in the database, you must for a reindexing
571         * of all data in the database or resources may not be
572         * searchable.
573         * </p>
574         */
575        public boolean isAllowContainsSearches() {
576                return myAllowContainsSearches;
577        }
578
579        /**
580         * If enabled, the server will support the use of :contains searches,
581         * which are helpful but can have adverse effects on performance.
582         * <p>
583         * Default is <code>false</code> (Note that prior to HAPI FHIR
584         * 3.5.0 the default was <code>true</code>)
585         * </p>
586         * <p>
587         * Note: If you change this value after data already has
588         * already been stored in the database, you must for a reindexing
589         * of all data in the database or resources may not be
590         * searchable.
591         * </p>
592         */
593        public void setAllowContainsSearches(boolean theAllowContainsSearches) {
594                this.myAllowContainsSearches = theAllowContainsSearches;
595        }
596
597        /**
598         * If enabled, the server will support the use of :mdm search parameter qualifier on Reference Search Parameters.
599         * This Parameter Qualifier is HAPI-specific, and not defined anywhere in the FHIR specification. Using this qualifier
600         * will result in an MDM expansion being done on the reference, which will expand the search scope. For example, if Patient/1
601         * is MDM-matched to Patient/2 and you execute the search:
602         * Observation?subject:mdm=Patient/1 , you will receive observations for both Patient/1 and Patient/2.
603         * <p>
604         * Default is <code>false</code>
605         * </p>
606         *
607         * @since 5.4.0
608         */
609        public boolean isAllowMdmExpansion() {
610                return myAllowMdmExpansion;
611        }
612
613        /**
614         * If enabled, the server will support the use of :mdm search parameter qualifier on Reference Search Parameters.
615         * This Parameter Qualifier is HAPI-specific, and not defined anywhere in the FHIR specification. Using this qualifier
616         * will result in an MDM expansion being done on the reference, which will expand the search scope. For example, if Patient/1
617         * is MDM-matched to Patient/2 and you execute the search:
618         * Observation?subject:mdm=Patient/1 , you will receive observations for both Patient/1 and Patient/2.
619         * <p>
620         * Default is <code>false</code>
621         * </p>
622         *
623         * @since 5.4.0
624         */
625        public void setAllowMdmExpansion(boolean theAllowMdmExpansion) {
626                myAllowMdmExpansion = theAllowMdmExpansion;
627        }
628
629        /**
630         * If set to <code>true</code> (default is <code>false</code>) the server will allow
631         * resources to have references to external servers. For example if this server is
632         * running at <code>http://example.com/fhir</code> and this setting is set to
633         * <code>true</code> the server will allow a Patient resource to be saved with a
634         * Patient.organization value of <code>http://foo.com/Organization/1</code>.
635         * <p>
636         * Under the default behaviour if this value has not been changed, the above
637         * resource would be rejected by the server because it requires all references
638         * to be resolvable on the local server.
639         * </p>
640         * <p>
641         * Note that external references will be indexed by the server and may be searched
642         * (e.g. <code>Patient:organization</code>), but
643         * chained searches (e.g. <code>Patient:organization.name</code>) will not work across
644         * these references.
645         * </p>
646         * <p>
647         * It is recommended to also set {@link #setTreatBaseUrlsAsLocal(Set)} if this value
648         * is set to <code>true</code>
649         * </p>
650         *
651         * @see #setTreatBaseUrlsAsLocal(Set)
652         * @see #setAllowExternalReferences(boolean)
653         */
654        public boolean isAllowExternalReferences() {
655                return myAllowExternalReferences;
656        }
657
658        /**
659         * If set to <code>true</code> (default is <code>false</code>) the server will allow
660         * resources to have references to external servers. For example if this server is
661         * running at <code>http://example.com/fhir</code> and this setting is set to
662         * <code>true</code> the server will allow a Patient resource to be saved with a
663         * Patient.organization value of <code>http://foo.com/Organization/1</code>.
664         * <p>
665         * Under the default behaviour if this value has not been changed, the above
666         * resource would be rejected by the server because it requires all references
667         * to be resolvable on the local server.
668         * </p>
669         * <p>
670         * Note that external references will be indexed by the server and may be searched
671         * (e.g. <code>Patient:organization</code>), but
672         * chained searches (e.g. <code>Patient:organization.name</code>) will not work across
673         * these references.
674         * </p>
675         * <p>
676         * It is recommended to also set {@link #setTreatBaseUrlsAsLocal(Set)} if this value
677         * is set to <code>true</code>
678         * </p>
679         *
680         * @see #setTreatBaseUrlsAsLocal(Set)
681         * @see #setAllowExternalReferences(boolean)
682         */
683        public void setAllowExternalReferences(boolean theAllowExternalReferences) {
684                myAllowExternalReferences = theAllowExternalReferences;
685        }
686
687        /**
688         * This setting may be used to advise the server that any references found in
689         * resources that have any of the base URLs given here will be replaced with
690         * simple local references.
691         * <p>
692         * For example, if the set contains the value <code>http://example.com/base/</code>
693         * and a resource is submitted to the server that contains a reference to
694         * <code>http://example.com/base/Patient/1</code>, the server will automatically
695         * convert this reference to <code>Patient/1</code>
696         * </p>
697         * <p>
698         * Note that this property has different behaviour from {@link StorageSettings#getTreatReferencesAsLogical()}
699         * </p>
700         *
701         * @see #getTreatReferencesAsLogical()
702         */
703        public Set<String> getTreatBaseUrlsAsLocal() {
704                return myTreatBaseUrlsAsLocal;
705        }
706
707        /**
708         * This setting may be used to advise the server that any references found in
709         * resources that have any of the base URLs given here will be replaced with
710         * simple local references.
711         * <p>
712         * For example, if the set contains the value <code>http://example.com/base/</code>
713         * and a resource is submitted to the server that contains a reference to
714         * <code>http://example.com/base/Patient/1</code>, the server will automatically
715         * convert this reference to <code>Patient/1</code>
716         * </p>
717         *
718         * @param theTreatBaseUrlsAsLocal The set of base URLs. May be <code>null</code>, which
719         *                                means no references will be treated as external
720         */
721        public void setTreatBaseUrlsAsLocal(Set<String> theTreatBaseUrlsAsLocal) {
722                if (theTreatBaseUrlsAsLocal != null) {
723                        for (String next : theTreatBaseUrlsAsLocal) {
724                                validateTreatBaseUrlsAsLocal(next);
725                        }
726                }
727
728                HashSet<String> treatBaseUrlsAsLocal = new HashSet<>();
729                for (String next : defaultIfNull(theTreatBaseUrlsAsLocal, new HashSet<String>())) {
730                        while (next.endsWith("/")) {
731                                next = next.substring(0, next.length() - 1);
732                        }
733                        treatBaseUrlsAsLocal.add(next);
734                }
735                myTreatBaseUrlsAsLocal = treatBaseUrlsAsLocal;
736        }
737
738        /**
739         * Add a value to the {@link #setTreatReferencesAsLogical(Set) logical references list}.
740         *
741         * @see #setTreatReferencesAsLogical(Set)
742         */
743        public void addTreatReferencesAsLogical(String theTreatReferencesAsLogical) {
744                validateTreatBaseUrlsAsLocal(theTreatReferencesAsLogical);
745
746                if (myTreatReferencesAsLogical == null) {
747                        myTreatReferencesAsLogical = new HashSet<>();
748                }
749                myTreatReferencesAsLogical.add(theTreatReferencesAsLogical);
750        }
751
752        /**
753         * This setting may be used to advise the server that any references found in
754         * resources that have any of the base URLs given here will be treated as logical
755         * references instead of being treated as real references.
756         * <p>
757         * A logical reference is a reference which is treated as an identifier, and
758         * does not neccesarily resolve. See <a href="http://hl7.org/fhir/references.html">references</a> for
759         * a description of logical references. For example, the valueset
760         * <a href="http://hl7.org/fhir/valueset-quantity-comparator.html">valueset-quantity-comparator</a> is a logical
761         * reference.
762         * </p>
763         * <p>
764         * Values for this field may take either of the following forms:
765         * </p>
766         * <ul>
767         * <li><code>http://example.com/some-url</code> <b>(will be matched exactly)</b></li>
768         * <li><code>http://example.com/some-base*</code> <b>(will match anything beginning with the part before the *)</b></li>
769         * </ul>
770         *
771         * @see #DEFAULT_LOGICAL_BASE_URLS Default values for this property
772         */
773        public Set<String> getTreatReferencesAsLogical() {
774                return myTreatReferencesAsLogical;
775        }
776
777        /**
778         * This setting may be used to advise the server that any references found in
779         * resources that have any of the base URLs given here will be treated as logical
780         * references instead of being treated as real references.
781         * <p>
782         * A logical reference is a reference which is treated as an identifier, and
783         * does not neccesarily resolve. See <a href="http://hl7.org/fhir/references.html">references</a> for
784         * a description of logical references. For example, the valueset
785         * <a href="http://hl7.org/fhir/valueset-quantity-comparator.html">valueset-quantity-comparator</a> is a logical
786         * reference.
787         * </p>
788         * <p>
789         * Values for this field may take either of the following forms:
790         * </p>
791         * <ul>
792         * <li><code>http://example.com/some-url</code> <b>(will be matched exactly)</b></li>
793         * <li><code>http://example.com/some-base*</code> <b>(will match anything beginning with the part before the *)</b></li>
794         * </ul>
795         *
796         * @see #DEFAULT_LOGICAL_BASE_URLS Default values for this property
797         */
798        public StorageSettings setTreatReferencesAsLogical(Set<String> theTreatReferencesAsLogical) {
799                myTreatReferencesAsLogical = theTreatReferencesAsLogical;
800                return this;
801        }
802
803        /**
804         * <p>
805         * Should searches use the integer field {@code SP_VALUE_LOW_DATE_ORDINAL} and {@code SP_VALUE_HIGH_DATE_ORDINAL} in
806         * {@link ResourceIndexedSearchParamDate} when resolving searches where all predicates are using
807         * precision of {@link TemporalPrecisionEnum#DAY}.
808         * <p>
809         * For example, if enabled, the search of {@code Observation?date=2020-02-25} will cause the date to be collapsed down to an
810         * integer representing the ordinal date {@code 20200225}. It would then be compared against {@link ResourceIndexedSearchParamDate#getValueLowDateOrdinal()}
811         * and {@link ResourceIndexedSearchParamDate#getValueHighDateOrdinal()}
812         * </p>
813         * Default is {@literal true} beginning in HAPI FHIR 5.0.0
814         * </p>
815         *
816         * @since 5.0.0
817         */
818        public boolean getUseOrdinalDatesForDayPrecisionSearches() {
819                return myUseOrdinalDatesForDayPrecisionSearches;
820        }
821
822        /**
823         * <p>
824         * Should searches use the integer field {@code SP_VALUE_LOW_DATE_ORDINAL} and {@code SP_VALUE_HIGH_DATE_ORDINAL} in
825         * {@link ResourceIndexedSearchParamDate} when resolving searches where all predicates are using
826         * precision of {@link TemporalPrecisionEnum#DAY}.
827         * <p>
828         * For example, if enabled, the search of {@code Observation?date=2020-02-25} will cause the date to be collapsed down to an
829         * ordinal {@code 20200225}. It would then be compared against {@link ResourceIndexedSearchParamDate#getValueLowDateOrdinal()}
830         * and {@link ResourceIndexedSearchParamDate#getValueHighDateOrdinal()}
831         * </p>
832         * Default is {@literal true} beginning in HAPI FHIR 5.0.0
833         * </p>
834         *
835         * @since 5.0.0
836         */
837        public void setUseOrdinalDatesForDayPrecisionSearches(boolean theUseOrdinalDates) {
838                myUseOrdinalDatesForDayPrecisionSearches = theUseOrdinalDates;
839        }
840
841        /**
842         * If set to <code>true</code> (default is <code>false</code>), when indexing SearchParameter values for token SearchParameter,
843         * the string component to support the <code>:text</code> modifier will be disabled. This means that the following fields
844         * will not be indexed for tokens:
845         * <ul>
846         *    <li>CodeableConcept.text</li>
847         *    <li>Coding.display</li>
848         *    <li>Identifier.use.text</li>
849         * </ul>
850         *
851         * @since 5.0.0
852         */
853        public boolean isSuppressStringIndexingInTokens() {
854                return mySuppressStringIndexingInTokens;
855        }
856
857        /**
858         * If set to <code>true</code> (default is <code>false</code>), when indexing SearchParameter values for token SearchParameter,
859         * the string component to support the <code>:text</code> modifier will be disabled. This means that the following fields
860         * will not be indexed for tokens:
861         * <ul>
862         *    <li>CodeableConcept.text</li>
863         *    <li>Coding.display</li>
864         *    <li>Identifier.use.text</li>
865         * </ul>
866         *
867         * @since 5.0.0
868         */
869        public void setSuppressStringIndexingInTokens(boolean theSuppressStringIndexingInTokens) {
870                mySuppressStringIndexingInTokens = theSuppressStringIndexingInTokens;
871        }
872
873        /**
874         * When indexing a Period (e.g. Encounter.period) where the period has an upper bound
875         * but not a lower bound, a canned "start of time" value can be used as the lower bound
876         * in order to allow range searches to correctly identify all values in the range.
877         * <p>
878         * The default value for this is {@link #DEFAULT_PERIOD_INDEX_START_OF_TIME} which
879         * is probably good enough for almost any application, but this can be changed if
880         * needed.
881         * </p>
882         * <p>
883         * Note the following database documented limitations:
884         *    <ul>
885         *       <li>JDBC Timestamp Datatype Low Value -4713 and High Value 9999</li>
886         *       <li>MySQL 8: the range for DATETIME values is '1000-01-01 00:00:00.000000' to '9999-12-31 23:59:59.999999`</li>
887         *       <li>Postgresql 12: Timestamp [without time zone] Low Value 4713 BC and High Value 294276 AD</li>
888         *       <li>Oracle: Timestamp Low Value 4712 BC and High Value 9999 CE</li>
889         *       <li>H2: datetime2 Low Value -4713 and High Value 9999</li>
890         *     </ul>
891         * </p>
892         *
893         * @see #getPeriodIndexEndOfTime()
894         * @since 5.1.0
895         */
896        public IPrimitiveType<Date> getPeriodIndexStartOfTime() {
897                return myPeriodIndexStartOfTime;
898        }
899
900        /**
901         * When indexing a Period (e.g. Encounter.period) where the period has an upper bound
902         * but not a lower bound, a canned "start of time" value can be used as the lower bound
903         * in order to allow range searches to correctly identify all values in the range.
904         * <p>
905         * The default value for this is {@link #DEFAULT_PERIOD_INDEX_START_OF_TIME} which
906         * is probably good enough for almost any application, but this can be changed if
907         * needed.
908         * </p>
909         * <p>
910         * Note the following database documented limitations:
911         *    <ul>
912         *       <li>JDBC Timestamp Datatype Low Value -4713 and High Value 9999</li>
913         *       <li>MySQL 8: the range for DATETIME values is '1000-01-01 00:00:00.000000' to '9999-12-31 23:59:59.999999`</li>
914         *       <li>Postgresql 12: Timestamp [without time zone] Low Value 4713 BC and High Value 294276 AD</li>
915         *       <li>Oracle: Timestamp Low Value 4712 BC and High Value 9999 CE</li>
916         *       <li>H2: datetime2 Low Value -4713 and High Value 9999</li>
917         *     </ul>
918         * </p>
919         *
920         * @see #getPeriodIndexEndOfTime()
921         * @since 5.1.0
922         */
923        public void setPeriodIndexStartOfTime(IPrimitiveType<Date> thePeriodIndexStartOfTime) {
924                Validate.notNull(thePeriodIndexStartOfTime, "thePeriodIndexStartOfTime must not be null");
925                myPeriodIndexStartOfTime = thePeriodIndexStartOfTime;
926        }
927
928        /**
929         * When indexing a Period (e.g. Encounter.period) where the period has a lower bound
930         * but not an upper bound, a canned "end of time" value can be used as the upper bound
931         * in order to allow range searches to correctly identify all values in the range.
932         * <p>
933         * The default value for this is {@link #DEFAULT_PERIOD_INDEX_START_OF_TIME} which
934         * is probably good enough for almost any application, but this can be changed if
935         * needed.
936         * </p>
937         * <p>
938         * Note the following database documented limitations:
939         *    <ul>
940         *       <li>JDBC Timestamp Datatype Low Value -4713 and High Value 9999</li>
941         *       <li>MySQL 8: the range for DATETIME values is '1000-01-01 00:00:00.000000' to '9999-12-31 23:59:59.999999`</li>
942         *       <li>Postgresql 12: Timestamp [without time zone] Low Value 4713 BC and High Value 294276 AD</li>
943         *       <li>Oracle: Timestamp Low Value 4712 BC and High Value 9999 CE</li>
944         *       <li>H2: datetime2 Low Value -4713 and High Value 9999</li>
945         *     </ul>
946         * </p>
947         *
948         * @see #getPeriodIndexStartOfTime()
949         * @since 5.1.0
950         */
951        public IPrimitiveType<Date> getPeriodIndexEndOfTime() {
952                return myPeriodIndexEndOfTime;
953        }
954
955        /**
956         * When indexing a Period (e.g. Encounter.period) where the period has an upper bound
957         * but not a lower bound, a canned "start of time" value can be used as the lower bound
958         * in order to allow range searches to correctly identify all values in the range.
959         * <p>
960         * The default value for this is {@link #DEFAULT_PERIOD_INDEX_START_OF_TIME} which
961         * is probably good enough for almost any application, but this can be changed if
962         * needed.
963         * </p>
964         * <p>
965         * Note the following database documented limitations:
966         *    <ul>
967         *       <li>JDBC Timestamp Datatype Low Value -4713 and High Value 9999</li>
968         *       <li>MySQL 8: the range for DATETIME values is '1000-01-01 00:00:00.000000' to '9999-12-31 23:59:59.999999`</li>
969         *       <li>Postgresql 12: Timestamp [without time zone] Low Value 4713 BC and High Value 294276 AD</li>
970         *       <li>Oracle: Timestamp Low Value 4712 BC and High Value 9999 CE</li>
971         *       <li>H2: datetime2 Low Value -4713 and High Value 9999</li>
972         *     </ul>
973         * </p>
974         *
975         * @see #getPeriodIndexStartOfTime()
976         * @since 5.1.0
977         */
978        public void setPeriodIndexEndOfTime(IPrimitiveType<Date> thePeriodIndexEndOfTime) {
979                Validate.notNull(thePeriodIndexEndOfTime, "thePeriodIndexEndOfTime must not be null");
980                myPeriodIndexEndOfTime = thePeriodIndexEndOfTime;
981        }
982
983        /**
984         * Toggles whether Quantity searches support value normalization when using valid UCUM coded values.
985         *
986         * <p>
987         * The default value is {@link NormalizedQuantitySearchLevel#NORMALIZED_QUANTITY_SEARCH_NOT_SUPPORTED} which is current behavior.
988         * </p>
989         * <p>
990         * Here is the UCUM service support level
991         *    <ul>
992         *       <li>{@link NormalizedQuantitySearchLevel#NORMALIZED_QUANTITY_SEARCH_NOT_SUPPORTED}, default, Quantity is stored in {@link ResourceIndexedSearchParamQuantity} only and it is used by searching.</li>
993         *       <li>{@link NormalizedQuantitySearchLevel#NORMALIZED_QUANTITY_STORAGE_SUPPORTED}, Quantity is stored in both {@link ResourceIndexedSearchParamQuantity} and {@link ResourceIndexedSearchParamQuantityNormalized}, but {@link ResourceIndexedSearchParamQuantity} is used by searching.</li>
994         *       <li>{@link NormalizedQuantitySearchLevel#NORMALIZED_QUANTITY_SEARCH_SUPPORTED}, Quantity is stored in both {@link ResourceIndexedSearchParamQuantity} and {@link ResourceIndexedSearchParamQuantityNormalized}, {@link ResourceIndexedSearchParamQuantityNormalized} is used by searching.</li>
995         *     </ul>
996         * </p>
997         *
998         * @since 5.3.0
999         */
1000        public NormalizedQuantitySearchLevel getNormalizedQuantitySearchLevel() {
1001                return myNormalizedQuantitySearchLevel;
1002        }
1003
1004        /**
1005         * Toggles whether Quantity searches support value normalization when using valid UCUM coded values.
1006         *
1007         * <p>
1008         * The default value is {@link NormalizedQuantitySearchLevel#NORMALIZED_QUANTITY_SEARCH_NOT_SUPPORTED} which is current behavior.
1009         * </p>
1010         * <p>
1011         * Here is the UCUM service support level
1012         *    <ul>
1013         *       <li>{@link NormalizedQuantitySearchLevel#NORMALIZED_QUANTITY_SEARCH_NOT_SUPPORTED}, default, Quantity is stored in {@link ResourceIndexedSearchParamQuantity} only and it is used by searching.</li>
1014         *       <li>{@link NormalizedQuantitySearchLevel#NORMALIZED_QUANTITY_STORAGE_SUPPORTED}, Quantity is stored in both {@link ResourceIndexedSearchParamQuantity} and {@link ResourceIndexedSearchParamQuantityNormalized}, but {@link ResourceIndexedSearchParamQuantity} is used by searching.</li>
1015         *       <li>{@link NormalizedQuantitySearchLevel#NORMALIZED_QUANTITY_SEARCH_SUPPORTED}, Quantity is stored in both {@link ResourceIndexedSearchParamQuantity} and {@link ResourceIndexedSearchParamQuantityNormalized}, {@link ResourceIndexedSearchParamQuantityNormalized} is used by searching.</li>
1016         *     </ul>
1017         * </p>
1018         *
1019         * @since 5.3.0
1020         */
1021        public void setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel theNormalizedQuantitySearchLevel) {
1022                myNormalizedQuantitySearchLevel = theNormalizedQuantitySearchLevel;
1023        }
1024
1025        /**
1026         * When set with resource paths (e.g. <code>"Observation.subject"</code>), any references found at the given paths
1027         * will automatically have versions appended. The version used will be the current version of the given resource.
1028         *
1029         * @since 5.3.0
1030         */
1031        public Set<String> getAutoVersionReferenceAtPaths() {
1032                return myAutoVersionReferenceAtPaths;
1033        }
1034
1035        /**
1036         * When set with resource paths (e.g. <code>"Observation.subject"</code>), any references found at the given paths
1037         * will automatically have versions appended. The version used will be the current version of the given resource.
1038         * <p>
1039         * Versions will only be added if the reference does not already have a version, so any versioned references
1040         * supplied by the client will take precedence over the automatic current version.
1041         * </p>
1042         * <p>
1043         * Note that for this setting to be useful, the {@link ParserOptions}
1044         * {@link ParserOptions#getDontStripVersionsFromReferencesAtPaths() DontStripVersionsFromReferencesAtPaths}
1045         * option must also be set.
1046         * </p>
1047         *
1048         * @param thePaths A collection of reference paths for which the versions will be appended automatically
1049         *                 when serializing, e.g. "Patient.managingOrganization" or "AuditEvent.object.reference". Note that
1050         *                 only resource name and field names with dots separating is allowed here (no repetition
1051         *                 indicators, FluentPath expressions, etc.)
1052         * @since 5.3.0
1053         */
1054        public void setAutoVersionReferenceAtPaths(String... thePaths) {
1055                Set<String> paths = Collections.emptySet();
1056                if (thePaths != null) {
1057                        paths = new HashSet<>(Arrays.asList(thePaths));
1058                }
1059                setAutoVersionReferenceAtPaths(paths);
1060        }
1061
1062        /**
1063         * When set with resource paths (e.g. <code>"Observation.subject"</code>), any references found at the given paths
1064         * will automatically have versions appended. The version used will be the current version of the given resource.
1065         * <p>
1066         * Versions will only be added if the reference does not already have a version, so any versioned references
1067         * supplied by the client will take precedence over the automatic current version.
1068         * </p>
1069         * <p>
1070         * Note that for this setting to be useful, the {@link ParserOptions}
1071         * {@link ParserOptions#getDontStripVersionsFromReferencesAtPaths() DontStripVersionsFromReferencesAtPaths}
1072         * option must also be set
1073         * </p>
1074         *
1075         * @param thePaths A collection of reference paths for which the versions will be appended automatically
1076         *                 when serializing, e.g. "Patient.managingOrganization" or "AuditEvent.object.reference". Note that
1077         *                 only resource name and field names with dots separating is allowed here (no repetition
1078         *                 indicators, FluentPath expressions, etc.)
1079         * @since 5.3.0
1080         */
1081        public void setAutoVersionReferenceAtPaths(Set<String> thePaths) {
1082                Set<String> paths = defaultIfNull(thePaths, Collections.emptySet());
1083                Map<String, Set<String>> byType = new HashMap<>();
1084                for (String nextPath : paths) {
1085                        int doxIdx = nextPath.indexOf('.');
1086                        Validate.isTrue(doxIdx > 0, "Invalid path for auto-version reference at path: %s", nextPath);
1087                        String type = nextPath.substring(0, doxIdx);
1088                        byType.computeIfAbsent(type, t -> new HashSet<>()).add(nextPath);
1089                }
1090
1091                myAutoVersionReferenceAtPaths = paths;
1092                myTypeToAutoVersionReferenceAtPaths = byType;
1093        }
1094
1095        /**
1096         * Returns a sub-collection of {@link #getAutoVersionReferenceAtPaths()} containing only paths
1097         * for the given resource type.
1098         *
1099         * @since 5.3.0
1100         */
1101        public Set<String> getAutoVersionReferenceAtPathsByResourceType(String theResourceType) {
1102                Validate.notEmpty(theResourceType, "theResourceType must not be null or empty");
1103                Set<String> retVal = myTypeToAutoVersionReferenceAtPaths.get(theResourceType);
1104                retVal = defaultIfNull(retVal, Collections.emptySet());
1105                return retVal;
1106        }
1107
1108        /**
1109         * Should searches with <code>_include</code> respect versioned references, and pull the specific requested version.
1110         * This may have performance impacts on heavily loaded systems.
1111         *
1112         * @since 5.3.0
1113         */
1114        public boolean isRespectVersionsForSearchIncludes() {
1115                return myRespectVersionsForSearchIncludes;
1116        }
1117
1118        /**
1119         * Should searches with <code>_include</code> respect versioned references, and pull the specific requested version.
1120         * This may have performance impacts on heavily loaded systems.
1121         *
1122         * @since 5.3.0
1123         */
1124        public void setRespectVersionsForSearchIncludes(boolean theRespectVersionsForSearchIncludes) {
1125                myRespectVersionsForSearchIncludes = theRespectVersionsForSearchIncludes;
1126        }
1127
1128        /**
1129         * If enabled, "Uplifted Refchains" will be enabled. This feature causes
1130         * HAPI FHIR to generate indexes for stored resources that include the current
1131         * value of the target of a chained reference, such as "Encounter?subject.name".
1132         *
1133         * @since 6.6.0
1134         */
1135        public boolean isIndexOnUpliftedRefchains() {
1136                return myIndexOnUpliftedRefchains;
1137        }
1138
1139        /**
1140         * If enabled, "Uplifted Refchains" will be enabled. This feature causes
1141         * HAPI FHIR to generate indexes for stored resources that include the current
1142         * value of the target of a chained reference, such as "Encounter?subject.name".
1143         *
1144         * @since 6.6.0
1145         */
1146        public void setIndexOnUpliftedRefchains(boolean theIndexOnUpliftedRefchains) {
1147                myIndexOnUpliftedRefchains = theIndexOnUpliftedRefchains;
1148        }
1149
1150        /**
1151         * Should indexing and searching on contained resources be enabled on this server.
1152         * This may have performance impacts, and should be enabled only if it is needed. Default is <code>false</code>.
1153         *
1154         * @since 5.4.0
1155         */
1156        public boolean isIndexOnContainedResources() {
1157                return myIndexOnContainedResources;
1158        }
1159
1160        /**
1161         * Should indexing and searching on contained resources be enabled on this server.
1162         * This may have performance impacts, and should be enabled only if it is needed. Default is <code>false</code>.
1163         *
1164         * @since 5.4.0
1165         */
1166        public void setIndexOnContainedResources(boolean theIndexOnContainedResources) {
1167                myIndexOnContainedResources = theIndexOnContainedResources;
1168        }
1169
1170        /**
1171         * Should recursive indexing and searching on contained resources be enabled on this server.
1172         * This may have performance impacts, and should be enabled only if it is needed. Default is <code>false</code>.
1173         *
1174         * @since 5.6.0
1175         */
1176        public boolean isIndexOnContainedResourcesRecursively() {
1177                return myIndexOnContainedResourcesRecursively;
1178        }
1179
1180        /**
1181         * Should recursive indexing and searching on contained resources be enabled on this server.
1182         * This may have performance impacts, and should be enabled only if it is needed. Default is <code>false</code>.
1183         *
1184         * @since 5.6.0
1185         */
1186        public void setIndexOnContainedResourcesRecursively(boolean theIndexOnContainedResourcesRecursively) {
1187                myIndexOnContainedResourcesRecursively = theIndexOnContainedResourcesRecursively;
1188        }
1189
1190        /**
1191         * If this is disabled by setting this to {@literal false} (default is {@literal true}),
1192         * the server will not automatically implement and support search parameters that
1193         * are not explcitly created in the repository.
1194         * <p>
1195         * Disabling this can have a dramatic improvement on performance (especially write performance)
1196         * in servers that only need to support a small number of search parameters, or no search parameters at all.
1197         * Disabling this obviously reduces the options for searching however.
1198         * </p>
1199         *
1200         * @since 5.7.0
1201         */
1202        public boolean isAutoSupportDefaultSearchParams() {
1203                return myAutoSupportDefaultSearchParams;
1204        }
1205
1206        /**
1207         * If this is disabled by setting this to {@literal false} (default is {@literal true}),
1208         * the server will not automatically implement and support search parameters that
1209         * are not explicitly created in the repository.
1210         * <p>
1211         * Disabling this can have a dramatic improvement on performance (especially write performance)
1212         * in servers that only need to support a small number of search parameters, or no search parameters at all.
1213         * Disabling this obviously reduces the options for searching however.
1214         * </p>
1215         *
1216         * @since 5.7.0
1217         */
1218        public void setAutoSupportDefaultSearchParams(boolean theAutoSupportDefaultSearchParams) {
1219                myAutoSupportDefaultSearchParams = theAutoSupportDefaultSearchParams;
1220        }
1221
1222        /**
1223         * @return Should the {@literal _language} SearchParameter be supported on this server? Defaults to {@literal false}.
1224         * @since 7.0.0
1225         */
1226        public boolean isLanguageSearchParameterEnabled() {
1227                return myLanguageSearchParameterEnabled;
1228        }
1229
1230        /**
1231         * Should the {@literal _language} SearchParameter be supported on this server? Defaults to {@literal false}.
1232         *
1233         * @since 7.0.0
1234         */
1235        public void setLanguageSearchParameterEnabled(boolean theLanguageSearchParameterEnabled) {
1236                myLanguageSearchParameterEnabled = theLanguageSearchParameterEnabled;
1237        }
1238
1239        /**
1240         * @return true if the filter is enabled for resources installed via package installer, false otherwise
1241         * @since 7.0.0
1242         */
1243        public boolean isValidateResourceStatusForPackageUpload() {
1244                return myValidateResourceStatusForPackageUpload;
1245        }
1246
1247        /**
1248         * Should resources being installed via package installer be filtered.
1249         * @since 7.0.0
1250         */
1251        public void setValidateResourceStatusForPackageUpload(boolean theValidateResourceStatusForPackageUpload) {
1252                myValidateResourceStatusForPackageUpload = theValidateResourceStatusForPackageUpload;
1253        }
1254
1255        private static void validateTreatBaseUrlsAsLocal(String theUrl) {
1256                Validate.notBlank(theUrl, "Base URL must not be null or empty");
1257
1258                int starIdx = theUrl.indexOf('*');
1259                if (starIdx != -1) {
1260                        if (starIdx != theUrl.length() - 1) {
1261                                throw new IllegalArgumentException(Msg.code(1525)
1262                                                + "Base URL wildcard character (*) can only appear at the end of the string: " + theUrl);
1263                        }
1264                }
1265        }
1266
1267        public enum IndexEnabledEnum {
1268                ENABLED,
1269                DISABLED
1270        }
1271
1272        public enum TagStorageModeEnum {
1273
1274                /**
1275                 * A separate set of tags is stored for each resource version
1276                 */
1277                VERSIONED,
1278
1279                /**
1280                 * A single set of tags is shared by all resource versions
1281                 */
1282                NON_VERSIONED,
1283
1284                /**
1285                 * Tags are stored directly in the resource body (in the {@literal ResourceHistoryTable}
1286                 * entry for the resource, meaning that they are not indexed separately, and are versioned with the rest
1287                 * of the resource.
1288                 */
1289                INLINE
1290        }
1291}