Start work on FT search

This commit is contained in:
James Agnew 2015-10-06 06:46:51 -04:00
parent 0958ab6f16
commit 8e8db1801c
53 changed files with 193 additions and 22 deletions

View File

@ -0,0 +1,11 @@
package ca.uhn.fhir.jpa.dao;
import ca.uhn.fhir.rest.server.IBundleProvider;
public interface ISearchDao {
public static final String FULL_TEXT_PARAM_NAME = "fullTextSearch";
IBundleProvider search(SearchParameterMap theParams);
}

View File

@ -0,0 +1,53 @@
package ca.uhn.fhir.jpa.dao;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.PersistenceContextType;
import org.hibernate.search.jpa.FullTextEntityManager;
import org.hibernate.search.jpa.FullTextQuery;
import org.hibernate.search.query.dsl.QueryBuilder;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.springframework.transaction.annotation.Transactional;
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamString;
import ca.uhn.fhir.rest.server.IBundleProvider;
public class SearchDao extends BaseHapiFhirDao<IBaseResource> implements ISearchDao {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchDao.class);
@PersistenceContext(type = PersistenceContextType.TRANSACTION)
private EntityManager myEntityManager;
@Transactional()
@Override
public IBundleProvider search(SearchParameterMap theParams) {
FullTextEntityManager em = org.hibernate.search.jpa.Search.getFullTextEntityManager(myEntityManager);
for (String nextParamName : theParams.keySet()) {
if (nextParamName.equals(FULL_TEXT_PARAM_NAME)) {
QueryBuilder qb = em.getSearchFactory().buildQueryBuilder().forEntity(ResourceIndexedSearchParamString.class).get();
org.apache.lucene.search.Query luceneQuery = qb
.keyword()
.onFields("myValueComplete")
.matching("AAAS")
.createQuery();
// wrap Lucene query in a javax.persistence.Query
FullTextQuery jpaQuery = em.createFullTextQuery(luceneQuery, ResourceIndexedSearchParamString.class);
// execute search
List<?> result = jpaQuery.getResultList();
for (Object object : result) {
ourLog.info(""+ object);
}
}
}
return null;
}
}

View File

@ -46,7 +46,6 @@ public class SearchParameterMap extends LinkedHashMap<String, List<List<? extend
private Set<Include> myIncludes;
private DateRangeParam myLastUpdated;
private Set<Include> myRevIncludes;
private SortSpec mySort;
public void add(String theName, IQueryParameterAnd<?> theAnd) {

View File

@ -30,6 +30,8 @@ import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.MappedSuperclass;
import org.hibernate.search.annotations.Field;
@MappedSuperclass
public abstract class BaseResourceIndexedSearchParam implements Serializable {
@ -42,6 +44,7 @@ public abstract class BaseResourceIndexedSearchParam implements Serializable {
@Column(name = "SP_ID")
private Long myId;
@Field
@Column(name = "SP_NAME", length = MAX_SP_NAME, nullable=false)
private String myParamName;
@ -49,9 +52,11 @@ public abstract class BaseResourceIndexedSearchParam implements Serializable {
@JoinColumn(name = "RES_ID", referencedColumnName="RES_ID")
private ResourceTable myResource;
@Field
@Column(name = "RES_ID", insertable = false, updatable = false)
private Long myResourcePid;
@Field
@Column(name = "RES_TYPE", nullable=false)
private String myResourceType;

View File

@ -28,8 +28,11 @@ 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.search.annotations.Field;
import org.hibernate.search.annotations.Indexed;
//@formatter:off
@Indexed
@Entity
@Table(name = "HFJ_SPIDX_COORDS" /* , indexes = { @Index(name = "IDX_SP_TOKEN", columnList = "SP_SYSTEM,SP_VALUE") } */)
@org.hibernate.annotations.Table(appliesTo = "HFJ_SPIDX_COORDS", indexes = {
@ -43,9 +46,11 @@ public class ResourceIndexedSearchParamCoords extends BaseResourceIndexedSearchP
private static final long serialVersionUID = 1L;
@Column(name = "SP_LATITUDE")
@Field
public double myLatitude;
@Column(name = "SP_LONGITUDE")
@Field
public double myLongitude;
public ResourceIndexedSearchParamCoords() {

View File

@ -32,8 +32,11 @@ 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.search.annotations.Field;
import org.hibernate.search.annotations.Indexed;
//@formatter:off
@Indexed
@Entity
@Table(name = "HFJ_SPIDX_DATE" /*, indexes= {@Index(name="IDX_SP_DATE", columnList= "SP_VALUE_LOW,SP_VALUE_HIGH")}*/)
@org.hibernate.annotations.Table(appliesTo = "HFJ_SPIDX_DATE", indexes= {
@ -46,15 +49,23 @@ public class ResourceIndexedSearchParamDate extends BaseResourceIndexedSearchPar
@Column(name = "SP_VALUE_HIGH", nullable = true)
@Temporal(TemporalType.TIMESTAMP)
@Field
public Date myValueHigh;
@Column(name = "SP_VALUE_LOW", nullable = true)
@Temporal(TemporalType.TIMESTAMP)
@Field
public Date myValueLow;
/**
* Constructor
*/
public ResourceIndexedSearchParamDate() {
}
/**
* Constructor
*/
public ResourceIndexedSearchParamDate(String theName, Date theLow, Date theHigh) {
setParamName(theName);
setValueLow(theLow);

View File

@ -30,8 +30,11 @@ 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.search.annotations.Field;
import org.hibernate.search.annotations.Indexed;
//@formatter:off
@Indexed
@Entity
@Table(name = "HFJ_SPIDX_NUMBER" /*, indexes= {@Index(name="IDX_SP_NUMBER", columnList="SP_VALUE")}*/ )
@org.hibernate.annotations.Table(appliesTo = "HFJ_SPIDX_NUMBER", indexes= {
@ -43,6 +46,7 @@ public class ResourceIndexedSearchParamNumber extends BaseResourceIndexedSearchP
private static final long serialVersionUID = 1L;
@Column(name = "SP_VALUE", nullable = true)
@Field
public BigDecimal myValue;
public ResourceIndexedSearchParamNumber() {

View File

@ -30,8 +30,11 @@ 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.search.annotations.Field;
import org.hibernate.search.annotations.Indexed;
//@formatter:off
@Indexed
@Entity
@Table(name = "HFJ_SPIDX_QUANTITY" /*, indexes= {@Index(name="IDX_SP_NUMBER", columnList="SP_VALUE")}*/ )
@org.hibernate.annotations.Table(appliesTo = "HFJ_SPIDX_QUANTITY", indexes= {
@ -40,15 +43,20 @@ import org.apache.commons.lang3.builder.ToStringStyle;
//@formatter:on
public class ResourceIndexedSearchParamQuantity extends BaseResourceIndexedSearchParam {
private static final int MAX_LENGTH = 200;
private static final long serialVersionUID = 1L;
@Column(name = "SP_SYSTEM", nullable = true, length = 100)
@Column(name = "SP_SYSTEM", nullable = true, length = MAX_LENGTH)
@Field
public String mySystem;
@Column(name = "SP_UNITS", nullable = true, length = 100)
@Column(name = "SP_UNITS", nullable = true, length = MAX_LENGTH)
@Field
public String myUnits;
@Column(name = "SP_VALUE", nullable = true)
@Field
public BigDecimal myValue;
public ResourceIndexedSearchParamQuantity() {

View File

@ -29,7 +29,10 @@ 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.search.annotations.Field;
import org.hibernate.search.annotations.Indexed;
@Indexed
@Entity
@Table(name = "HFJ_SPIDX_STRING"/* , indexes= {@Index(name="IDX_SP_STRING", columnList="SP_VALUE_NORMALIZED")} */)
@org.hibernate.annotations.Table(appliesTo = "HFJ_SPIDX_STRING", indexes = {
@ -37,16 +40,20 @@ import org.apache.commons.lang3.builder.ToStringStyle;
})
public class ResourceIndexedSearchParamString extends BaseResourceIndexedSearchParam {
public static final int MAX_LENGTH = 100;
public static final int MAX_LENGTH = 200;
private static final long serialVersionUID = 1L;
@Column(name = "SP_VALUE_EXACT", length = 100, nullable = true)
public String myValueExact;
@Column(name = "SP_VALUE_EXACT", length = MAX_LENGTH, nullable = true)
private String myValueExact;
@Column(name = "SP_VALUE_NORMALIZED", length = MAX_LENGTH, nullable = true)
public String myValueNormalized;
private String myValueNormalized;
@Column(name="SP_VALUE_EXACT", insertable=false, updatable=false)
@Field
private String myValueComplete;
public ResourceIndexedSearchParamString() {
}
@ -97,6 +104,7 @@ public class ResourceIndexedSearchParamString extends BaseResourceIndexedSearchP
throw new IllegalArgumentException("Value is too long: " + theValueExact.length());
}
myValueExact = theValueExact;
myValueComplete = theValueExact;
}
public void setValueNormalized(String theValueNormalized) {

View File

@ -29,21 +29,26 @@ 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.search.annotations.Field;
import org.hibernate.search.annotations.Indexed;
@Indexed
@Entity
@Table(name = "HFJ_SPIDX_TOKEN" /* , indexes = { @Index(name = "IDX_SP_TOKEN", columnList = "SP_SYSTEM,SP_VALUE") } */)
@org.hibernate.annotations.Table(appliesTo = "HFJ_SPIDX_TOKEN", indexes = { @org.hibernate.annotations.Index(name = "IDX_SP_TOKEN", columnNames = { "RES_TYPE", "SP_NAME", "SP_SYSTEM", "SP_VALUE" }),
@org.hibernate.annotations.Index(name = "IDX_SP_TOKEN_UNQUAL", columnNames = { "RES_TYPE", "SP_NAME", "SP_VALUE" }) })
public class ResourceIndexedSearchParamToken extends BaseResourceIndexedSearchParam {
public static final int MAX_LENGTH = 100;
public static final int MAX_LENGTH = 200;
private static final long serialVersionUID = 1L;
@Field()
@Column(name = "SP_SYSTEM", nullable = true, length = MAX_LENGTH)
public String mySystem;
@Column(name = "SP_VALUE", nullable = true, length = 100)
@Field()
@Column(name = "SP_VALUE", nullable = true, length = MAX_LENGTH)
public String myValue;
public ResourceIndexedSearchParamToken() {

View File

@ -28,8 +28,12 @@ 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.search.annotations.Analyzer;
import org.hibernate.search.annotations.Field;
import org.hibernate.search.annotations.Indexed;
//@formatter:off
@Indexed
@Entity
@Table(name = "HFJ_SPIDX_URI" /* , indexes = { @Index(name = "IDX_SP_TOKEN", columnList = "SP_SYSTEM,SP_VALUE") } */)
@org.hibernate.annotations.Table(appliesTo = "HFJ_SPIDX_URI", indexes = {
@ -43,6 +47,7 @@ public class ResourceIndexedSearchParamUri extends BaseResourceIndexedSearchPara
private static final long serialVersionUID = 1L;
@Column(name = "SP_URI", nullable = true, length = MAX_LENGTH)
@Field()
public String myUri;
public ResourceIndexedSearchParamUri() {

View File

@ -34,28 +34,22 @@ import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.Index;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import org.hibernate.annotations.Index;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
//@formatter:off
@Entity
@Table(name = "HFJ_RESOURCE", uniqueConstraints = {})
@Inheritance(strategy = InheritanceType.JOINED)
@org.hibernate.annotations.Table(appliesTo = "HFJ_RESOURCE",
indexes = {
@Index(name = "IDX_RES_DATE", columnNames = { "RES_UPDATED" }),
@Index(name = "IDX_RES_LANG", columnNames = { "RES_TYPE", "RES_LANGUAGE" }),
@Index(name = "IDX_RES_PROFILE", columnNames = { "RES_PROFILE" }),
@Index(name = "IDX_INDEXSTATUS", columnNames = { "SP_INDEX_STATUS" })
})
@Table(name = "HFJ_RESOURCE", uniqueConstraints = {}, indexes= {
@Index(name = "IDX_RES_DATE", columnList="RES_UPDATED"),
@Index(name = "IDX_RES_LANG", columnList="RES_TYPE,RES_LANGUAGE"),
@Index(name = "IDX_RES_PROFILE", columnList="RES_PROFILE"),
@Index(name = "IDX_INDEXSTATUS", columnList="SP_INDEX_STATUS")
})
//@formatter:on
public class ResourceTable extends BaseHasResource implements Serializable {
private static final int MAX_LANGUAGE_LENGTH = 20;

View File

@ -0,0 +1,21 @@
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:websocket="http://www.springframework.org/schema/websocket"
xmlns:task="http://www.springframework.org/schema/task"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/websocket http://www.springframework.org/schema/websocket/spring-websocket.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd">
<context:annotation-config />
<tx:annotation-driven transaction-manager="myTxManagerDstu2" />
<bean id="mySearchDaoDstu2" class="ca.uhn.fhir.jpa.dao.SearchDao" />
</beans>

View File

@ -0,0 +1,39 @@
package ca.uhn.fhir.jpa.dao;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import ca.uhn.fhir.model.dstu2.resource.Patient;
import ca.uhn.fhir.rest.param.StringAndListParam;
import ca.uhn.fhir.rest.param.StringOrListParam;
import ca.uhn.fhir.rest.param.StringParam;
@ContextConfiguration(locations = { "classpath:fhir-spring-search-config-dstu2.xml" })
public class FhirSearchDaoDstu2Test extends BaseJpaDstu2Test {
@Autowired
private ISearchDao mySearchDao;
@Test
public void testStringSearch() {
{
Patient patient = new Patient();
patient.addIdentifier().setSystem("urn:system").setValue("001");
patient.addName().addGiven("testSearchStringParamWithNonNormalized_h\u00F6ra");
patient.addName().addFamily("AAAS");
myPatientDao.create(patient);
}
{
Patient patient = new Patient();
patient.addIdentifier().setSystem("urn:system").setValue("002");
patient.addName().addGiven("testSearchStringParamWithNonNormalized_HORA");
myPatientDao.create(patient);
}
SearchParameterMap map = new SearchParameterMap();
map.add(ISearchDao.FULL_TEXT_PARAM_NAME, new StringAndListParam().addAnd(new StringOrListParam().addOr(new StringParam("AAA"))));
mySearchDao.search(map);
}
}

View File

@ -35,6 +35,9 @@
<property name="hibernate.cache.use_query_cache" value="false" />
<property name="hibernate.cache.use_second_level_cache" value="false" />
<property name="hibernate.cache.use_structured_entries" value="false" />
<property name="hibernate.search.default.directory_provider" value="filesystem"/>
<property name="hibernate.search.default.indexBase" value="./lucene_indexes"/>
<!--
<property name="hibernate.ejb.naming_strategy" value="ca.uhn.fhir.jpa.util.CustomNamingStrategy" />
-->