From 4ab958a58f3eecc3bb374269ce8bce4eb205ea8e Mon Sep 17 00:00:00 2001 From: James Agnew Date: Wed, 16 Oct 2024 18:08:43 -0400 Subject: [PATCH] Add hash identity token mode (#6375) * Add hash identity token mode * Spotless * Test fix --- .../6375-add-hash-identity-token-mode.yaml | 6 ++ .../predicate/TokenPredicateBuilder.java | 21 +++++++ .../model/entity/ResourceHistoryTable.java | 6 +- .../jpa/model/entity/ResourceHistoryTag.java | 4 +- .../ResourceIndexedComboStringUnique.java | 6 +- .../ResourceIndexedComboTokenNonUnique.java | 6 +- .../ResourceIndexedSearchParamCoords.java | 4 +- .../ResourceIndexedSearchParamDate.java | 4 +- .../ResourceIndexedSearchParamNumber.java | 4 +- .../ResourceIndexedSearchParamQuantity.java | 6 +- ...eIndexedSearchParamQuantityNormalized.java | 6 +- .../ResourceIndexedSearchParamString.java | 4 +- .../ResourceIndexedSearchParamToken.java | 4 +- .../entity/ResourceIndexedSearchParamUri.java | 4 +- .../fhir/jpa/model/entity/ResourceLink.java | 4 +- .../fhir/jpa/model/entity/ResourceTag.java | 4 +- .../entity/SearchParamPresentEntity.java | 6 +- .../r4/FhirResourceDaoR4SearchSqlTest.java | 55 +++++++++++++++++++ .../ca/uhn/fhir/jpa/test/BaseJpaTest.java | 2 + .../predicate/TokenPredicateBuilderTest.java | 2 + .../jpa/api/config/JpaStorageSettings.java | 33 +++++++++++ 21 files changed, 161 insertions(+), 30 deletions(-) create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_6_0/6375-add-hash-identity-token-mode.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_6_0/6375-add-hash-identity-token-mode.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_6_0/6375-add-hash-identity-token-mode.yaml new file mode 100644 index 00000000000..afcbe53ed49 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_6_0/6375-add-hash-identity-token-mode.yaml @@ -0,0 +1,6 @@ +--- +type: add +issue: 6375 +title: "A new experimental JPA setting has been added to JpaStorageSettings which + causes searches for token SearchParameters to include a predicate on the + HASH_IDENTITY column even if it is not needed because other hashes are in use." diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/TokenPredicateBuilder.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/TokenPredicateBuilder.java index fcd2aaac902..414d108e985 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/TokenPredicateBuilder.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/TokenPredicateBuilder.java @@ -48,6 +48,7 @@ import ca.uhn.fhir.rest.param.TokenParamModifier; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.MethodNotAllowedException; import ca.uhn.fhir.util.FhirVersionIndependentConcept; +import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.Sets; import com.healthmarketscience.sqlbuilder.BinaryCondition; import com.healthmarketscience.sqlbuilder.Condition; @@ -78,6 +79,7 @@ public class TokenPredicateBuilder extends BaseSearchParamPredicateBuilder { private final DbColumn myColumnHashValue; private final DbColumn myColumnSystem; private final DbColumn myColumnValue; + private final DbColumn myColumnHashIdentity; @Autowired private IValidationSupport myValidationSupport; @@ -97,6 +99,7 @@ public class TokenPredicateBuilder extends BaseSearchParamPredicateBuilder { public TokenPredicateBuilder(SearchQueryBuilder theSearchSqlBuilder) { super(theSearchSqlBuilder, theSearchSqlBuilder.addTable("HFJ_SPIDX_TOKEN")); myColumnResId = getTable().addColumn("RES_ID"); + myColumnHashIdentity = getTable().addColumn("HASH_IDENTITY"); myColumnHashSystem = getTable().addColumn("HASH_SYS"); myColumnHashSystemAndValue = getTable().addColumn("HASH_SYS_AND_VALUE"); myColumnHashValue = getTable().addColumn("HASH_VALUE"); @@ -104,6 +107,16 @@ public class TokenPredicateBuilder extends BaseSearchParamPredicateBuilder { myColumnValue = getTable().addColumn("SP_VALUE"); } + @Override + public DbColumn getColumnHashIdentity() { + return myColumnHashIdentity; + } + + @VisibleForTesting + public void setStorageSettingsForUnitTest(JpaStorageSettings theStorageSettings) { + myStorageSettings = theStorageSettings; + } + @Override public DbColumn getResourceIdColumn() { return myColumnResId; @@ -257,6 +270,14 @@ public class TokenPredicateBuilder extends BaseSearchParamPredicateBuilder { } else { predicate = createPredicateOrList(theResourceName, paramName, sortedCodesList, true); + + if (myStorageSettings.isIncludeHashIdentityForTokenSearches()) { + long hashIdentity = BaseResourceIndexedSearchParam.calculateHashIdentity( + getPartitionSettings(), theRequestPartitionId, theResourceName, paramName); + Condition hashIdentityPredicate = + BinaryCondition.equalTo(getColumnHashIdentity(), generatePlaceholder(hashIdentity)); + predicate = QueryParameterUtils.toAndPredicate(hashIdentityPredicate, predicate); + } } return predicate; diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceHistoryTable.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceHistoryTable.java index 8ef37d8cdca..d88cb054541 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceHistoryTable.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceHistoryTable.java @@ -38,13 +38,13 @@ import jakarta.persistence.Lob; import jakarta.persistence.ManyToOne; import jakarta.persistence.OneToMany; import jakarta.persistence.OneToOne; -import jakarta.persistence.SequenceGenerator; import jakarta.persistence.Table; import jakarta.persistence.Transient; import jakarta.persistence.UniqueConstraint; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; import org.hibernate.Length; +import org.hibernate.annotations.GenericGenerator; import org.hibernate.annotations.OptimisticLock; import java.io.Serializable; @@ -78,7 +78,9 @@ public class ResourceHistoryTable extends BaseHasResource implements Serializabl private static final long serialVersionUID = 1L; @Id - @SequenceGenerator(name = "SEQ_RESOURCE_HISTORY_ID", sequenceName = "SEQ_RESOURCE_HISTORY_ID") + @GenericGenerator( + name = "SEQ_RESOURCE_HISTORY_ID", + type = ca.uhn.fhir.jpa.model.dialect.HapiSequenceStyleGenerator.class) @GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_RESOURCE_HISTORY_ID") @Column(name = "PID") private Long myId; diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceHistoryTag.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceHistoryTag.java index 0f2d8c70b51..c78e68a4a90 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceHistoryTag.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceHistoryTag.java @@ -29,9 +29,9 @@ import jakarta.persistence.Id; import jakarta.persistence.Index; import jakarta.persistence.JoinColumn; import jakarta.persistence.ManyToOne; -import jakarta.persistence.SequenceGenerator; import jakarta.persistence.Table; import jakarta.persistence.UniqueConstraint; +import org.hibernate.annotations.GenericGenerator; import java.io.Serializable; @@ -49,7 +49,7 @@ public class ResourceHistoryTag extends BaseTag implements Serializable { private static final long serialVersionUID = 1L; - @SequenceGenerator(name = "SEQ_HISTORYTAG_ID", sequenceName = "SEQ_HISTORYTAG_ID") + @GenericGenerator(name = "SEQ_HISTORYTAG_ID", type = ca.uhn.fhir.jpa.model.dialect.HapiSequenceStyleGenerator.class) @GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_HISTORYTAG_ID") @Id @Column(name = "PID") diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedComboStringUnique.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedComboStringUnique.java index 400eea6eac3..8e980d69391 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedComboStringUnique.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedComboStringUnique.java @@ -29,7 +29,6 @@ import jakarta.persistence.Id; import jakarta.persistence.Index; import jakarta.persistence.JoinColumn; import jakarta.persistence.ManyToOne; -import jakarta.persistence.SequenceGenerator; import jakarta.persistence.Table; import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.builder.CompareToBuilder; @@ -37,6 +36,7 @@ import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; +import org.hibernate.annotations.GenericGenerator; import org.hl7.fhir.instance.model.api.IIdType; /** @@ -76,7 +76,9 @@ public class ResourceIndexedComboStringUnique extends BaseResourceIndexedCombo public static final String IDX_IDXCMPSTRUNIQ_STRING = "IDX_IDXCMPSTRUNIQ_STRING"; public static final String IDX_IDXCMPSTRUNIQ_RESOURCE = "IDX_IDXCMPSTRUNIQ_RESOURCE"; - @SequenceGenerator(name = "SEQ_IDXCMPSTRUNIQ_ID", sequenceName = "SEQ_IDXCMPSTRUNIQ_ID") + @GenericGenerator( + name = "SEQ_IDXCMPSTRUNIQ_ID", + type = ca.uhn.fhir.jpa.model.dialect.HapiSequenceStyleGenerator.class) @GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_IDXCMPSTRUNIQ_ID") @Id @Column(name = "PID") diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedComboTokenNonUnique.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedComboTokenNonUnique.java index 2ed9371b4ee..07026856503 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedComboTokenNonUnique.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedComboTokenNonUnique.java @@ -31,13 +31,13 @@ import jakarta.persistence.Id; import jakarta.persistence.Index; import jakarta.persistence.JoinColumn; import jakarta.persistence.ManyToOne; -import jakarta.persistence.SequenceGenerator; import jakarta.persistence.Table; import jakarta.persistence.Transient; import org.apache.commons.lang3.builder.CompareToBuilder; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apache.commons.lang3.builder.ToStringBuilder; +import org.hibernate.annotations.GenericGenerator; @Entity @Table( @@ -51,7 +51,9 @@ import org.apache.commons.lang3.builder.ToStringBuilder; public class ResourceIndexedComboTokenNonUnique extends BaseResourceIndexedCombo implements Comparable, IResourceIndexComboSearchParameter { - @SequenceGenerator(name = "SEQ_IDXCMBTOKNU_ID", sequenceName = "SEQ_IDXCMBTOKNU_ID") + @GenericGenerator( + name = "SEQ_IDXCMBTOKNU_ID", + type = ca.uhn.fhir.jpa.model.dialect.HapiSequenceStyleGenerator.class) @GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_IDXCMBTOKNU_ID") @Id @Column(name = "PID") diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamCoords.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamCoords.java index bf6e7baaed3..647490860ab 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamCoords.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamCoords.java @@ -35,12 +35,12 @@ import jakarta.persistence.Id; import jakarta.persistence.Index; import jakarta.persistence.JoinColumn; import jakarta.persistence.ManyToOne; -import jakarta.persistence.SequenceGenerator; import jakarta.persistence.Table; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; +import org.hibernate.annotations.GenericGenerator; @Embeddable @EntityListeners(IndexStorageOptimizationListener.class) @@ -67,7 +67,7 @@ public class ResourceIndexedSearchParamCoords extends BaseResourceIndexedSearchP public Double myLongitude; @Id - @SequenceGenerator(name = "SEQ_SPIDX_COORDS", sequenceName = "SEQ_SPIDX_COORDS") + @GenericGenerator(name = "SEQ_SPIDX_COORDS", type = ca.uhn.fhir.jpa.model.dialect.HapiSequenceStyleGenerator.class) @GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_SPIDX_COORDS") @Column(name = "SP_ID") private Long myId; diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamDate.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamDate.java index 45259d4a9f5..3a98bc66dfe 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamDate.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamDate.java @@ -39,7 +39,6 @@ import jakarta.persistence.Id; import jakarta.persistence.Index; import jakarta.persistence.JoinColumn; import jakarta.persistence.ManyToOne; -import jakarta.persistence.SequenceGenerator; import jakarta.persistence.Table; import jakarta.persistence.Temporal; import jakarta.persistence.TemporalType; @@ -49,6 +48,7 @@ import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; +import org.hibernate.annotations.GenericGenerator; import org.hibernate.search.mapper.pojo.mapping.definition.annotation.FullTextField; import org.hl7.fhir.r4.model.DateTimeType; @@ -107,7 +107,7 @@ public class ResourceIndexedSearchParamDate extends BaseResourceIndexedSearchPar private transient String myOriginalValue; @Id - @SequenceGenerator(name = "SEQ_SPIDX_DATE", sequenceName = "SEQ_SPIDX_DATE") + @GenericGenerator(name = "SEQ_SPIDX_DATE", type = ca.uhn.fhir.jpa.model.dialect.HapiSequenceStyleGenerator.class) @GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_SPIDX_DATE") @Column(name = "SP_ID") private Long myId; diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamNumber.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamNumber.java index 902e3ac6c0c..393ab64d60e 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamNumber.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamNumber.java @@ -35,12 +35,12 @@ import jakarta.persistence.Id; import jakarta.persistence.Index; import jakarta.persistence.JoinColumn; import jakarta.persistence.ManyToOne; -import jakarta.persistence.SequenceGenerator; import jakarta.persistence.Table; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; +import org.hibernate.annotations.GenericGenerator; import org.hibernate.annotations.JdbcTypeCode; import org.hibernate.search.mapper.pojo.mapping.definition.annotation.ScaledNumberField; import org.hibernate.type.SqlTypes; @@ -68,7 +68,7 @@ public class ResourceIndexedSearchParamNumber extends BaseResourceIndexedSearchP public BigDecimal myValue; @Id - @SequenceGenerator(name = "SEQ_SPIDX_NUMBER", sequenceName = "SEQ_SPIDX_NUMBER") + @GenericGenerator(name = "SEQ_SPIDX_NUMBER", type = ca.uhn.fhir.jpa.model.dialect.HapiSequenceStyleGenerator.class) @GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_SPIDX_NUMBER") @Column(name = "SP_ID") private Long myId; diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamQuantity.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamQuantity.java index 0f1b2bd5568..02129a99600 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamQuantity.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamQuantity.java @@ -35,12 +35,12 @@ import jakarta.persistence.Id; import jakarta.persistence.Index; import jakarta.persistence.JoinColumn; import jakarta.persistence.ManyToOne; -import jakarta.persistence.SequenceGenerator; import jakarta.persistence.Table; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; +import org.hibernate.annotations.GenericGenerator; import org.hibernate.search.mapper.pojo.mapping.definition.annotation.ScaledNumberField; import java.math.BigDecimal; @@ -74,7 +74,9 @@ public class ResourceIndexedSearchParamQuantity extends BaseResourceIndexedSearc private static final long serialVersionUID = 1L; @Id - @SequenceGenerator(name = "SEQ_SPIDX_QUANTITY", sequenceName = "SEQ_SPIDX_QUANTITY") + @GenericGenerator( + name = "SEQ_SPIDX_QUANTITY", + type = ca.uhn.fhir.jpa.model.dialect.HapiSequenceStyleGenerator.class) @GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_SPIDX_QUANTITY") @Column(name = "SP_ID") private Long myId; diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamQuantityNormalized.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamQuantityNormalized.java index b235a86bc09..86ec8a7d9bd 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamQuantityNormalized.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamQuantityNormalized.java @@ -36,13 +36,13 @@ import jakarta.persistence.Id; import jakarta.persistence.Index; import jakarta.persistence.JoinColumn; import jakarta.persistence.ManyToOne; -import jakarta.persistence.SequenceGenerator; import jakarta.persistence.Table; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; import org.fhir.ucum.Pair; +import org.hibernate.annotations.GenericGenerator; import org.hibernate.search.mapper.pojo.mapping.definition.annotation.ScaledNumberField; import java.math.BigDecimal; @@ -80,7 +80,9 @@ public class ResourceIndexedSearchParamQuantityNormalized extends BaseResourceIn private static final long serialVersionUID = 1L; @Id - @SequenceGenerator(name = "SEQ_SPIDX_QUANTITY_NRML", sequenceName = "SEQ_SPIDX_QUANTITY_NRML") + @GenericGenerator( + name = "SEQ_SPIDX_QUANTITY_NRML", + type = ca.uhn.fhir.jpa.model.dialect.HapiSequenceStyleGenerator.class) @GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_SPIDX_QUANTITY_NRML") @Column(name = "SP_ID") private Long myId; diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamString.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamString.java index 69c8d3a1632..ba3312bb801 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamString.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamString.java @@ -37,12 +37,12 @@ import jakarta.persistence.Id; import jakarta.persistence.Index; import jakarta.persistence.JoinColumn; import jakarta.persistence.ManyToOne; -import jakarta.persistence.SequenceGenerator; import jakarta.persistence.Table; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; +import org.hibernate.annotations.GenericGenerator; import static ca.uhn.fhir.jpa.model.util.SearchParamHash.hashSearchParam; import static org.apache.commons.lang3.StringUtils.defaultString; @@ -78,7 +78,7 @@ public class ResourceIndexedSearchParamString extends BaseResourceIndexedSearchP private static final long serialVersionUID = 1L; @Id - @SequenceGenerator(name = "SEQ_SPIDX_STRING", sequenceName = "SEQ_SPIDX_STRING") + @GenericGenerator(name = "SEQ_SPIDX_STRING", type = ca.uhn.fhir.jpa.model.dialect.HapiSequenceStyleGenerator.class) @GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_SPIDX_STRING") @Column(name = "SP_ID") private Long myId; diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamToken.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamToken.java index cfe3d886249..e492efc9436 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamToken.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamToken.java @@ -39,13 +39,13 @@ import jakarta.persistence.JoinColumn; import jakarta.persistence.ManyToOne; import jakarta.persistence.PrePersist; import jakarta.persistence.PreUpdate; -import jakarta.persistence.SequenceGenerator; import jakarta.persistence.Table; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; +import org.hibernate.annotations.GenericGenerator; import org.hibernate.search.mapper.pojo.mapping.definition.annotation.FullTextField; import static ca.uhn.fhir.jpa.model.util.SearchParamHash.hashSearchParam; @@ -89,7 +89,7 @@ public class ResourceIndexedSearchParamToken extends BaseResourceIndexedSearchPa @SuppressWarnings("unused") @Id - @SequenceGenerator(name = "SEQ_SPIDX_TOKEN", sequenceName = "SEQ_SPIDX_TOKEN") + @GenericGenerator(name = "SEQ_SPIDX_TOKEN", type = ca.uhn.fhir.jpa.model.dialect.HapiSequenceStyleGenerator.class) @GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_SPIDX_TOKEN") @Column(name = "SP_ID") private Long myId; diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamUri.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamUri.java index f7fb75439a8..0db8b2ad2b3 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamUri.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamUri.java @@ -36,12 +36,12 @@ import jakarta.persistence.Id; import jakarta.persistence.Index; import jakarta.persistence.JoinColumn; import jakarta.persistence.ManyToOne; -import jakarta.persistence.SequenceGenerator; import jakarta.persistence.Table; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apache.commons.lang3.builder.ToStringBuilder; +import org.hibernate.annotations.GenericGenerator; import org.hibernate.search.mapper.pojo.mapping.definition.annotation.FullTextField; import static ca.uhn.fhir.jpa.model.util.SearchParamHash.hashSearchParam; @@ -76,7 +76,7 @@ public class ResourceIndexedSearchParamUri extends BaseResourceIndexedSearchPara public String myUri; @Id - @SequenceGenerator(name = "SEQ_SPIDX_URI", sequenceName = "SEQ_SPIDX_URI") + @GenericGenerator(name = "SEQ_SPIDX_URI", type = ca.uhn.fhir.jpa.model.dialect.HapiSequenceStyleGenerator.class) @GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_SPIDX_URI") @Column(name = "SP_ID") private Long myId; diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceLink.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceLink.java index 545d5bd7603..a101cc3011d 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceLink.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceLink.java @@ -31,7 +31,6 @@ import jakarta.persistence.Id; import jakarta.persistence.Index; import jakarta.persistence.JoinColumn; import jakarta.persistence.ManyToOne; -import jakarta.persistence.SequenceGenerator; import jakarta.persistence.Table; import jakarta.persistence.Temporal; import jakarta.persistence.TemporalType; @@ -39,6 +38,7 @@ import jakarta.persistence.Transient; import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; +import org.hibernate.annotations.GenericGenerator; import org.hibernate.search.mapper.pojo.mapping.definition.annotation.FullTextField; import org.hl7.fhir.instance.model.api.IIdType; @@ -63,7 +63,7 @@ public class ResourceLink extends BaseResourceIndex { public static final int SRC_PATH_LENGTH = 500; private static final long serialVersionUID = 1L; - @SequenceGenerator(name = "SEQ_RESLINK_ID", sequenceName = "SEQ_RESLINK_ID") + @GenericGenerator(name = "SEQ_RESLINK_ID", type = ca.uhn.fhir.jpa.model.dialect.HapiSequenceStyleGenerator.class) @GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_RESLINK_ID") @Id @Column(name = "PID") diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceTag.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceTag.java index dab71c775a4..0b2f9078169 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceTag.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceTag.java @@ -29,13 +29,13 @@ import jakarta.persistence.Id; import jakarta.persistence.Index; import jakarta.persistence.JoinColumn; import jakarta.persistence.ManyToOne; -import jakarta.persistence.SequenceGenerator; import jakarta.persistence.Table; import jakarta.persistence.UniqueConstraint; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; +import org.hibernate.annotations.GenericGenerator; @Entity @Table( @@ -53,7 +53,7 @@ public class ResourceTag extends BaseTag { private static final long serialVersionUID = 1L; - @SequenceGenerator(name = "SEQ_RESTAG_ID", sequenceName = "SEQ_RESTAG_ID") + @GenericGenerator(name = "SEQ_RESTAG_ID", type = ca.uhn.fhir.jpa.model.dialect.HapiSequenceStyleGenerator.class) @GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_RESTAG_ID") @Id @Column(name = "PID") diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/SearchParamPresentEntity.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/SearchParamPresentEntity.java index 3f931b56952..88d5616ef61 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/SearchParamPresentEntity.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/SearchParamPresentEntity.java @@ -31,7 +31,6 @@ import jakarta.persistence.Index; import jakarta.persistence.JoinColumn; import jakarta.persistence.ManyToOne; import jakarta.persistence.PrePersist; -import jakarta.persistence.SequenceGenerator; import jakarta.persistence.Table; import jakarta.persistence.Transient; import org.apache.commons.lang3.Validate; @@ -39,6 +38,7 @@ import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; +import org.hibernate.annotations.GenericGenerator; import java.io.Serializable; @@ -57,7 +57,9 @@ public class SearchParamPresentEntity extends BasePartitionable implements Seria private static final long serialVersionUID = 1L; @Id - @SequenceGenerator(name = "SEQ_RESPARMPRESENT_ID", sequenceName = "SEQ_RESPARMPRESENT_ID") + @GenericGenerator( + name = "SEQ_RESPARMPRESENT_ID", + type = ca.uhn.fhir.jpa.model.dialect.HapiSequenceStyleGenerator.class) @GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_RESPARMPRESENT_ID") @Column(name = "PID") private Long myId; diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchSqlTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchSqlTest.java index 36cc2c81483..cd9b1955cf8 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchSqlTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchSqlTest.java @@ -8,6 +8,7 @@ import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.jpa.test.BaseJpaR4Test; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.server.IBundleProvider; +import ca.uhn.fhir.rest.param.StringParam; import ca.uhn.fhir.rest.param.TokenParam; import ca.uhn.fhir.rest.param.UriParam; import org.hl7.fhir.instance.model.api.IIdType; @@ -18,6 +19,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -140,6 +142,38 @@ public class FhirResourceDaoR4SearchSqlTest extends BaseJpaR4Test { return myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(false, false); } + /** + * One regular search params - Doesn't need HFJ_RESOURCE as root + */ + @Test + public void testSingleRegularSearchParam() { + + myCaptureQueriesListener.clear(); + SearchParameterMap map = SearchParameterMap.newSynchronous(Patient.SP_NAME, new StringParam("FOO")); + myPatientDao.search(map); + assertEquals(1, myCaptureQueriesListener.countSelectQueries()); + String sql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(false, false); + assertEquals("SELECT t0.RES_ID FROM HFJ_SPIDX_STRING t0 WHERE ((t0.HASH_NORM_PREFIX = ?) AND (t0.SP_VALUE_NORMALIZED LIKE ?))", sql); + + } + + /** + * Two regular search params - Should use HFJ_RESOURCE as root + */ + @Test + public void testTwoRegularSearchParams() { + + myCaptureQueriesListener.clear(); + SearchParameterMap map = SearchParameterMap.newSynchronous() + .add(Patient.SP_NAME, new StringParam("FOO")) + .add(Patient.SP_GENDER, new TokenParam("a", "b")); + myPatientDao.search(map); + assertEquals(1, myCaptureQueriesListener.countSelectQueries()); + String sql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(false, false); + assertEquals("SELECT t1.RES_ID FROM HFJ_RESOURCE t1 INNER JOIN HFJ_SPIDX_STRING t0 ON (t1.RES_ID = t0.RES_ID) INNER JOIN HFJ_SPIDX_TOKEN t2 ON (t1.RES_ID = t2.RES_ID) WHERE (((t0.HASH_NORM_PREFIX = ?) AND (t0.SP_VALUE_NORMALIZED LIKE ?)) AND (t2.HASH_SYS_AND_VALUE = ?))", sql); + + + } @Test public void testSearchByProfile_VersionedMode() { @@ -207,6 +241,27 @@ public class FhirResourceDaoR4SearchSqlTest extends BaseJpaR4Test { myStorageSettings.setMarkResourcesForReindexingUponSearchParameterChange(reindexParamCache); } + @ParameterizedTest + @ValueSource(booleans = {true, false}) + public void testSearchByToken_IncludeHashIdentity(boolean theIncludeHashIdentity) { + // Setup + myStorageSettings.setIncludeHashIdentityForTokenSearches(theIncludeHashIdentity); + + // Test + myCaptureQueriesListener.clear(); + SearchParameterMap params = SearchParameterMap.newSynchronous(Patient.SP_IDENTIFIER, new TokenParam("http://foo", "bar")); + IBundleProvider outcome = myPatientDao.search(params, mySrd); + assertEquals(0, outcome.sizeOrThrowNpe()); + + // Verify + if (theIncludeHashIdentity) { + assertEquals("SELECT t0.RES_ID FROM HFJ_SPIDX_TOKEN t0 WHERE ((t0.HASH_IDENTITY = '7001889285610424179') AND (t0.HASH_SYS_AND_VALUE = '-2780914544385068076'))", myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false)); + } else { + assertEquals("SELECT t0.RES_ID FROM HFJ_SPIDX_TOKEN t0 WHERE (t0.HASH_SYS_AND_VALUE = '-2780914544385068076')", myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false)); + } + + } + public static class MyPartitionInterceptor { @Hook(STORAGE_PARTITION_IDENTIFY_ANY) diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/BaseJpaTest.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/BaseJpaTest.java index de0e82e76ae..2a338b75f58 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/BaseJpaTest.java +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/BaseJpaTest.java @@ -389,6 +389,8 @@ public abstract class BaseJpaTest extends BaseTest { JpaStorageSettings defaultConfig = new JpaStorageSettings(); myStorageSettings.setAdvancedHSearchIndexing(defaultConfig.isAdvancedHSearchIndexing()); myStorageSettings.setAllowContainsSearches(defaultConfig.isAllowContainsSearches()); + myStorageSettings.setIncludeHashIdentityForTokenSearches(defaultConfig.isIncludeHashIdentityForTokenSearches()); + } @AfterEach diff --git a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/search/builder/predicate/TokenPredicateBuilderTest.java b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/search/builder/predicate/TokenPredicateBuilderTest.java index c62e6554de4..7025e44f910 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/search/builder/predicate/TokenPredicateBuilderTest.java +++ b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/search/builder/predicate/TokenPredicateBuilderTest.java @@ -2,6 +2,7 @@ package ca.uhn.fhir.jpa.search.builder.predicate; import ca.uhn.fhir.context.RuntimeSearchParam; import ca.uhn.fhir.interceptor.model.RequestPartitionId; +import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; import ca.uhn.fhir.jpa.model.config.PartitionSettings; import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamToken; import ca.uhn.fhir.jpa.search.builder.sql.SearchQueryBuilder; @@ -58,6 +59,7 @@ public class TokenPredicateBuilderTest { when(mySearchQueryBuilder.addTable(Mockito.anyString())).thenReturn(table); when(mySearchQueryBuilder.getPartitionSettings()).thenReturn(new PartitionSettings()); myTokenPredicateBuilder = new TokenPredicateBuilder(mySearchQueryBuilder); + myTokenPredicateBuilder.setStorageSettingsForUnitTest(new JpaStorageSettings()); } @AfterEach diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/config/JpaStorageSettings.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/config/JpaStorageSettings.java index 78ec78d7c79..9b9e19034d0 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/config/JpaStorageSettings.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/config/JpaStorageSettings.java @@ -28,6 +28,7 @@ import ca.uhn.fhir.rest.api.SearchTotalModeEnum; import ca.uhn.fhir.system.HapiSystemProperties; import ca.uhn.fhir.util.HapiExtensions; import ca.uhn.fhir.validation.FhirValidator; +import com.google.common.annotations.Beta; import com.google.common.collect.Sets; import jakarta.annotation.Nonnull; import jakarta.annotation.Nullable; @@ -368,6 +369,16 @@ public class JpaStorageSettings extends StorageSettings { */ private boolean myWriteToLegacyLobColumns = false; + /** + * If this is enabled (default is {@literal false}), searches on token indexes will + * include the {@literal HASH_IDENTITY} column on all generated FHIR search query SQL. + * This is an experimental flag that may be changed or removed in a future release. + * + * @since 7.6.0 + */ + @Beta + private boolean myIncludeHashIdentityForTokenSearches = false; + /** * Constructor */ @@ -396,6 +407,28 @@ public class JpaStorageSettings extends StorageSettings { } } + /** + * If this is enabled (default is {@literal false}), searches on token indexes will + * include the {@literal HASH_IDENTITY} column on all generated FHIR search query SQL. + * This is an experimental flag that may be changed or removed in a future release. + * + * @since 7.6.0 + */ + public boolean isIncludeHashIdentityForTokenSearches() { + return myIncludeHashIdentityForTokenSearches; + } + + /** + * If this is enabled (default is {@literal false}), searches on token indexes will + * include the {@literal HASH_IDENTITY} column on all generated FHIR search query SQL. + * This is an experimental flag that may be changed or removed in a future release. + * + * @since 7.6.0 + */ + public void setIncludeHashIdentityForTokenSearches(boolean theIncludeHashIdentityForTokenSearches) { + myIncludeHashIdentityForTokenSearches = theIncludeHashIdentityForTokenSearches; + } + /** * @since 5.7.0 * @deprecated This setting no longer does anything as of HAPI FHIR 7.0.0