diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/BaseParam.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/BaseParam.java index 66e08a5ab70..88c00be3160 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/BaseParam.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/BaseParam.java @@ -40,7 +40,7 @@ abstract class BaseParam implements IQueryParameterType { @Override public final String getQueryParameterQualifier() { - if (myMissing != null) { + if (myMissing != null && myMissing.booleanValue()) { return Constants.PARAMQUALIFIER_MISSING; } return doGetQueryParameterQualifier(); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/StringParam.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/StringParam.java index fc475699ae4..56093b27194 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/StringParam.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/StringParam.java @@ -19,8 +19,7 @@ package ca.uhn.fhir.rest.param; * limitations under the License. * #L% */ - -import static org.apache.commons.lang3.StringUtils.*; +import static org.apache.commons.lang3.StringUtils.defaultString; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.builder.ToStringBuilder; @@ -34,6 +33,7 @@ public class StringParam extends BaseParam implements IQueryParameterType { private boolean myExact; private String myValue; + private boolean myContains; public StringParam() { } @@ -51,6 +51,8 @@ public class StringParam extends BaseParam implements IQueryParameterType { String doGetQueryParameterQualifier() { if (isExact()) { return Constants.PARAMQUALIFIER_STRING_EXACT; + } else if (isContains()) { + return Constants.PARAMQUALIFIER_STRING_CONTAINS; } else { return null; } @@ -68,6 +70,11 @@ public class StringParam extends BaseParam implements IQueryParameterType { } else { setExact(false); } + if (Constants.PARAMQUALIFIER_STRING_CONTAINS.equals(theQualifier)) { + setContains(true); + } else { + setContains(false); + } myValue = ParameterUtil.unescape(theValue); } @@ -91,12 +98,25 @@ public class StringParam extends BaseParam implements IQueryParameterType { return myExact; } - public void setExact(boolean theExact) { + public StringParam setExact(boolean theExact) { myExact = theExact; + if (myExact) { + setContains(false); + setMissing(null); + } + return this; } - public void setValue(String theValue) { + /** + * String parameter modifier :contains + */ + public boolean isContains() { + return myContains; + } + + public StringParam setValue(String theValue) { myValue = theValue; + return this; } @Override @@ -106,10 +126,25 @@ public class StringParam extends BaseParam implements IQueryParameterType { if (myExact) { builder.append("exact", myExact); } + if (myContains) { + builder.append("contains", myContains); + } if (getMissing() != null) { builder.append("missing", getMissing().booleanValue()); } return builder.toString(); } + /** + * String parameter modifier :contains + */ + public StringParam setContains(boolean theContains) { + myContains = theContains; + if (myContains) { + setExact(false); + setMissing(null); + } + return this; + } + } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/Constants.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/Constants.java index 7177f1d4cbe..e020a1bc936 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/Constants.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/Constants.java @@ -127,6 +127,7 @@ public class Constants { public static final String PARAMQUALIFIER_MISSING_FALSE = "false"; public static final String PARAMQUALIFIER_MISSING_TRUE = "true"; public static final String PARAMQUALIFIER_STRING_EXACT = ":exact"; + public static final String PARAMQUALIFIER_STRING_CONTAINS = ":contains"; public static final String PARAMQUALIFIER_TOKEN_TEXT = ":text"; public static final int STATUS_HTTP_200_OK = 200; public static final int STATUS_HTTP_201_CREATED = 201; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirSearchDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirSearchDao.java index f2dda1ff35f..034b7cea2e8 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirSearchDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirSearchDao.java @@ -101,7 +101,7 @@ public class FhirSearchDao extends BaseHapiFhirDao implements ISe for (List nextAndList : theParams.get(nextParamName)) { for (Iterator orIterator = nextAndList.iterator(); orIterator.hasNext();) { IQueryParameterType nextParam = orIterator.next(); - if (nextParam instanceof TokenParam && false) { + if (nextParam instanceof TokenParam) { TokenParam nextTokenParam = (TokenParam) nextParam; if (nextTokenParam.isText()) { orIterator.remove(); @@ -112,27 +112,26 @@ public class FhirSearchDao extends BaseHapiFhirDao implements ISe if (isNotBlank(theResourceName)) { bool.must(qb.keyword().onField("myResourceType").matching(theResourceName).createQuery()); } - +// //@formatter:off + String value = nextTokenParam.getValue().toLowerCase(); Query textQuery = qb .phrase() .withSlop(2) .onField("myValueText").boostedTo(4.0f) .andField("myValueTextEdgeNGram").boostedTo(2.0f) - .andField("myValueTextNGram").boostedTo(1.0f) - .sentence(nextTokenParam.getValue().toLowerCase()).createQuery(); +// .andField("myValueTextNGram").boostedTo(1.0f) + .sentence(value).createQuery(); bool.must(textQuery); //@formatter:on - FullTextQuery ftq = em.createFullTextQuery(bool.createQuery(), ResourceTable.class); - ftq.setProjection("myResourcePid"); + FullTextQuery ftq = em.createFullTextQuery(bool.createQuery(), ResourceIndexedSearchParamString.class); List resultList = ftq.getResultList(); pids = new ArrayList(); for (Object next : resultList) { - Object[] nextAsArray = (Object[]) next; - Long nextValue = (Long) nextAsArray[0]; - pids.add(nextValue); + ResourceIndexedSearchParamString nextAsArray = (ResourceIndexedSearchParamString) next; + pids.add(nextAsArray.getResourcePid()); } } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/BaseResourceIndexedSearchParam.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/BaseResourceIndexedSearchParam.java index 43c5333ac96..65fae6bcd4d 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/BaseResourceIndexedSearchParam.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/BaseResourceIndexedSearchParam.java @@ -47,20 +47,19 @@ public abstract class BaseResourceIndexedSearchParam implements Serializable { private Long myId; @Field - @Column(name = "SP_NAME", length = MAX_SP_NAME, nullable=false) + @Column(name = "SP_NAME", length = MAX_SP_NAME, nullable = false) private String myParamName; @ManyToOne(optional = false) - @JoinColumn(name = "RES_ID", referencedColumnName="RES_ID") + @JoinColumn(name = "RES_ID", referencedColumnName = "RES_ID") @ContainedIn private ResourceTable myResource; - @Field(store=Store.YES) @Column(name = "RES_ID", insertable = false, updatable = false) private Long myResourcePid; - @Field(store=Store.YES) - @Column(name = "RES_TYPE", nullable=false) + @Field() + @Column(name = "RES_TYPE", nullable = false) private String myResourceType; protected Long getId() { @@ -75,6 +74,10 @@ public abstract class BaseResourceIndexedSearchParam implements Serializable { return myResource; } + public Long getResourcePid() { + return myResourcePid; + } + public void setParamName(String theName) { myParamName = theName; } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamString.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamString.java index 1a8f7cf6f05..2d22e997330 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamString.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamString.java @@ -201,4 +201,5 @@ public class ResourceIndexedSearchParamString extends BaseResourceIndexedSearchP b.append("value", getValueNormalized()); return b.build(); } + } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/BaseJpaDstu2Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/BaseJpaDstu2Test.java index f9d53358aab..1149b1548d4 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/BaseJpaDstu2Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/BaseJpaDstu2Test.java @@ -191,6 +191,7 @@ public abstract class BaseJpaDstu2Test extends BaseJpaTest { public void beforeFlushFT() { FullTextEntityManager ftem = Search.getFullTextEntityManager(myEntityManager); ftem.purgeAll(ResourceTable.class); + ftem.purgeAll(ResourceIndexedSearchParamString.class); ftem.flushToIndexes(); myDaoConfig.setSchedulingDisabled(true); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu21/BaseJpaDstu21Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu21/BaseJpaDstu21Test.java index f7b93f3d247..33c7e19a1f7 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu21/BaseJpaDstu21Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu21/BaseJpaDstu21Test.java @@ -185,6 +185,7 @@ public abstract class BaseJpaDstu21Test extends BaseJpaTest { public void beforeFlushFT() { FullTextEntityManager ftem = Search.getFullTextEntityManager(myEntityManager); ftem.purgeAll(ResourceTable.class); + ftem.purgeAll(ResourceIndexedSearchParamString.class); ftem.flushToIndexes(); myDaoConfig.setSchedulingDisabled(true); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu21/FhirResourceDaoDstu21SearchFtTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu21/FhirResourceDaoDstu21SearchFtTest.java index 51a975d1131..b704eebf41a 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu21/FhirResourceDaoDstu21SearchFtTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu21/FhirResourceDaoDstu21SearchFtTest.java @@ -38,12 +38,12 @@ public class FhirResourceDaoDstu21SearchFtTest extends BaseJpaDstu21Test { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoDstu21SearchFtTest.class); @Test - @Ignore public void testCodeTextSearch() { Observation obs1 = new Observation(); obs1.getCode().setText("Systolic Blood Pressure"); obs1.setStatus(ObservationStatusEnum.FINAL); obs1.setValue(new QuantityDt(123)); + obs1.setComments("obs1"); IIdType id1 = myObservationDao.create(obs1).getId().toUnqualifiedVersionless(); Observation obs2 = new Observation(); @@ -55,10 +55,43 @@ public class FhirResourceDaoDstu21SearchFtTest extends BaseJpaDstu21Test { SearchParameterMap map; map = new SearchParameterMap(); - map.add(Observation.SP_CODE, new TokenParam(null, "Systolic").setText(true)); - assertThat(toUnqualifiedVersionlessIds(myPatientDao.search(map)), containsInAnyOrder(id1, id2)); - + map.add(Observation.SP_CODE, new TokenParam(null, "blood").setText(true)); + assertThat(toUnqualifiedVersionlessIds(myObservationDao.search(map)), containsInAnyOrder(id1, id2)); + + map = new SearchParameterMap(); + map.add(Observation.SP_CODE, new TokenParam(null, "blood").setText(true)); + assertThat(toUnqualifiedVersionlessIds(myPatientDao.search(map)), empty()); + + map = new SearchParameterMap(); + map.add(Observation.SP_CODE, new TokenParam(null, "blood").setText(true)); + map.add(Constants.PARAM_CONTENT, new StringParam("obs1")); + assertThat(toUnqualifiedVersionlessIds(myObservationDao.search(map)), containsInAnyOrder(id1)); + } + + @Test + @Ignore + public void testStringTextSearch() { + Observation obs1 = new Observation(); + obs1.getCode().setText("AAAAA"); + obs1.setValue(new StringDt("Systolic Blood Pressure")); + obs1.setStatus(ObservationStatusEnum.FINAL); + IIdType id1 = myObservationDao.create(obs1).getId().toUnqualifiedVersionless(); + + Observation obs2 = new Observation(); + obs1.getCode().setText("AAAAA"); + obs1.setValue(new StringDt("Diastolic Blood Pressure")); + obs2.setStatus(ObservationStatusEnum.FINAL); + IIdType id2 = myObservationDao.create(obs2).getId().toUnqualifiedVersionless(); + + SearchParameterMap map; + + map = new SearchParameterMap(); + map.add(Observation.SP_VALUE_STRING, new StringParam("sure").setContains(true)); + assertThat(toUnqualifiedVersionlessIds(myObservationDao.search(map)), containsInAnyOrder(id1, id2)); + + } + @Test public void testSuggestIgnoresBase64Content() { diff --git a/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/server/StringParameterTest.java b/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/server/StringParameterTest.java index 9e26d5be165..d64e462b0b4 100644 --- a/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/server/StringParameterTest.java +++ b/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/server/StringParameterTest.java @@ -36,6 +36,18 @@ public class StringParameterTest { private static Server ourServer; + @Test + public void testContains() { + StringParam sp = new StringParam("VAL"); + sp.setContains(true); + assertEquals(":contains", sp.getQueryParameterQualifier()); + + sp = new StringParam("VAL"); + sp.setValueAsQueryToken(":contains", "VAL"); + assertEquals(true, sp.isContains()); + assertEquals("VAL", sp.getValue()); + } + @Test public void testRawString() throws Exception { { diff --git a/pom.xml b/pom.xml index 4cb5894c9f8..ad590bd4675 100644 --- a/pom.xml +++ b/pom.xml @@ -742,7 +742,7 @@ true random -Dfile.encoding=UTF-8 - false +