Fix searching in JPA with _id and _content params
This commit is contained in:
parent
e1c62ef03e
commit
d710682fed
|
@ -20,18 +20,21 @@ package ca.uhn.fhir.jpa.dao;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import javax.persistence.*;
|
||||
|
||||
import ca.uhn.fhir.jpa.entity.ResourceTable;
|
||||
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.rest.param.StringParam;
|
||||
import ca.uhn.fhir.rest.param.TokenParam;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Sets;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.apache.lucene.analysis.Analyzer;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.apache.lucene.search.highlight.*;
|
||||
import org.apache.lucene.search.highlight.Formatter;
|
||||
import org.apache.lucene.search.highlight.*;
|
||||
import org.hibernate.search.jpa.FullTextEntityManager;
|
||||
import org.hibernate.search.jpa.FullTextQuery;
|
||||
import org.hibernate.search.query.dsl.BooleanJunction;
|
||||
|
@ -40,15 +43,12 @@ import org.hl7.fhir.dstu3.model.BaseResource;
|
|||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Sets;
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.PersistenceContext;
|
||||
import javax.persistence.PersistenceContextType;
|
||||
import java.util.*;
|
||||
|
||||
import ca.uhn.fhir.jpa.entity.ResourceTable;
|
||||
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.rest.param.StringParam;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
public class FulltextSearchSvcImpl extends BaseHapiFhirDao<IBaseResource> implements IFulltextSearchSvc {
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FulltextSearchSvcImpl.class);
|
||||
|
@ -62,7 +62,7 @@ public class FulltextSearchSvcImpl extends BaseHapiFhirDao<IBaseResource> implem
|
|||
public FulltextSearchSvcImpl() {
|
||||
super();
|
||||
}
|
||||
|
||||
|
||||
private void addTextSearch(QueryBuilder theQueryBuilder, BooleanJunction<?> theBoolean, List<List<? extends IQueryParameterType>> theTerms, String theFieldName, String theFieldNameEdgeNGram, String theFieldNameNGram) {
|
||||
if (theTerms == null) {
|
||||
return;
|
||||
|
@ -87,11 +87,12 @@ public class FulltextSearchSvcImpl extends BaseHapiFhirDao<IBaseResource> implem
|
|||
// .andField(theFieldNameNGram).boostedTo(1.0f)
|
||||
.sentence(terms.iterator().next().toLowerCase()).createQuery();
|
||||
//@formatter:on
|
||||
|
||||
|
||||
theBoolean.must(textQuery);
|
||||
} else {
|
||||
String joinedTerms = StringUtils.join(terms, ' ');
|
||||
theBoolean.must(theQueryBuilder.keyword().onField(theFieldName).matching(joinedTerms).createQuery()); }
|
||||
theBoolean.must(theQueryBuilder.keyword().onField(theFieldName).matching(joinedTerms).createQuery());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -145,7 +146,7 @@ public class FulltextSearchSvcImpl extends BaseHapiFhirDao<IBaseResource> implem
|
|||
return pids;
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
QueryBuilder qb = em.getSearchFactory().buildQueryBuilder().forEntity(ResourceTable.class).get();
|
||||
BooleanJunction<?> bool = qb.bool();
|
||||
|
||||
|
@ -183,7 +184,7 @@ public class FulltextSearchSvcImpl extends BaseHapiFhirDao<IBaseResource> implem
|
|||
List<?> result = jpaQuery.getResultList();
|
||||
|
||||
HashSet<Long> pidsSet = pids != null ? new HashSet<Long>(pids) : null;
|
||||
|
||||
|
||||
ArrayList<Long> retVal = new ArrayList<Long>();
|
||||
for (Object object : result) {
|
||||
Object[] nextArray = (Object[]) object;
|
||||
|
@ -201,8 +202,16 @@ public class FulltextSearchSvcImpl extends BaseHapiFhirDao<IBaseResource> implem
|
|||
|
||||
Long pid = null;
|
||||
if (theParams.get(BaseResource.SP_RES_ID) != null) {
|
||||
StringParam idParm = (StringParam) theParams.get(BaseResource.SP_RES_ID).get(0).get(0);
|
||||
pid = BaseHapiFhirDao.translateForcedIdToPid(theResourceName, idParm.getValue(), myForcedIdDao);
|
||||
String idParamValue;
|
||||
IQueryParameterType idParam = theParams.get(BaseResource.SP_RES_ID).get(0).get(0);
|
||||
if (idParam instanceof TokenParam) {
|
||||
TokenParam idParm = (TokenParam) idParam;
|
||||
idParamValue = idParm.getValue();
|
||||
} else {
|
||||
StringParam idParm = (StringParam) idParam;
|
||||
idParamValue = idParm.getValue();
|
||||
}
|
||||
pid = BaseHapiFhirDao.translateForcedIdToPid(theResourceName, idParamValue, myForcedIdDao);
|
||||
}
|
||||
|
||||
Long referencingPid = pid;
|
||||
|
@ -213,6 +222,19 @@ public class FulltextSearchSvcImpl extends BaseHapiFhirDao<IBaseResource> implem
|
|||
return retVal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDisabled() {
|
||||
try {
|
||||
FullTextEntityManager em = org.hibernate.search.jpa.Search.getFullTextEntityManager(myEntityManager);
|
||||
em.getSearchFactory().buildQueryBuilder().forEntity(ResourceTable.class).get();
|
||||
} catch (Exception e) {
|
||||
ourLog.trace("FullText test failed", e);
|
||||
ourLog.debug("Hibernate Search (Lucene) appears to be disabled on this server, fulltext will be disabled");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Transactional()
|
||||
@Override
|
||||
public List<Long> search(String theResourceName, SearchParameterMap theParams) {
|
||||
|
@ -238,18 +260,18 @@ public class FulltextSearchSvcImpl extends BaseHapiFhirDao<IBaseResource> implem
|
|||
QueryBuilder qb = em.getSearchFactory().buildQueryBuilder().forEntity(ResourceTable.class).get();
|
||||
|
||||
Query textQuery = qb
|
||||
.phrase()
|
||||
.withSlop(2)
|
||||
.onField("myContentText").boostedTo(4.0f)
|
||||
.andField("myContentTextEdgeNGram").boostedTo(2.0f)
|
||||
.andField("myContentTextNGram").boostedTo(1.0f)
|
||||
.andField("myContentTextPhonetic").boostedTo(0.5f)
|
||||
.sentence(theText.toLowerCase()).createQuery();
|
||||
|
||||
.phrase()
|
||||
.withSlop(2)
|
||||
.onField("myContentText").boostedTo(4.0f)
|
||||
.andField("myContentTextEdgeNGram").boostedTo(2.0f)
|
||||
.andField("myContentTextNGram").boostedTo(1.0f)
|
||||
.andField("myContentTextPhonetic").boostedTo(0.5f)
|
||||
.sentence(theText.toLowerCase()).createQuery();
|
||||
|
||||
Query query = qb.bool()
|
||||
.must(qb.keyword().onField("myResourceLinks.myTargetResourcePid").matching(pid).createQuery())
|
||||
.must(textQuery)
|
||||
.createQuery();
|
||||
.must(qb.keyword().onField("myResourceLinks.myTargetResourcePid").matching(pid).createQuery())
|
||||
.must(textQuery)
|
||||
.createQuery();
|
||||
|
||||
FullTextQuery ftq = em.createFullTextQuery(query, ResourceTable.class);
|
||||
ftq.setProjection("myContentText");
|
||||
|
@ -286,7 +308,7 @@ public class FulltextSearchSvcImpl extends BaseHapiFhirDao<IBaseResource> implem
|
|||
Collections.sort(suggestions);
|
||||
|
||||
Set<String> terms = Sets.newHashSet();
|
||||
for (Iterator<Suggestion> iter = suggestions.iterator(); iter.hasNext();) {
|
||||
for (Iterator<Suggestion> iter = suggestions.iterator(); iter.hasNext(); ) {
|
||||
String nextTerm = iter.next().getTerm().toLowerCase();
|
||||
if (!terms.add(nextTerm)) {
|
||||
iter.remove();
|
||||
|
@ -294,39 +316,11 @@ public class FulltextSearchSvcImpl extends BaseHapiFhirDao<IBaseResource> implem
|
|||
}
|
||||
|
||||
long delay = System.currentTimeMillis() - start;
|
||||
ourLog.info("Provided {} suggestions for term {} in {} ms", new Object[] { terms.size(), theText, delay });
|
||||
ourLog.info("Provided {} suggestions for term {} in {} ms", new Object[]{terms.size(), theText, delay});
|
||||
|
||||
return suggestions;
|
||||
}
|
||||
|
||||
public static class Suggestion implements Comparable<Suggestion> {
|
||||
public Suggestion(String theTerm, float theScore) {
|
||||
myTerm = theTerm;
|
||||
myScore = theScore;
|
||||
}
|
||||
|
||||
public String getTerm() {
|
||||
return myTerm;
|
||||
}
|
||||
|
||||
public float getScore() {
|
||||
return myScore;
|
||||
}
|
||||
|
||||
private String myTerm;
|
||||
private float myScore;
|
||||
|
||||
@Override
|
||||
public int compareTo(Suggestion theO) {
|
||||
return Float.compare(theO.myScore, myScore);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Suggestion[myTerm=" + myTerm + ", myScore=" + myScore + "]";
|
||||
}
|
||||
}
|
||||
|
||||
public class MySuggestionFormatter implements Formatter {
|
||||
|
||||
private List<Suggestion> mySuggestions;
|
||||
|
@ -340,26 +334,9 @@ public class FulltextSearchSvcImpl extends BaseHapiFhirDao<IBaseResource> implem
|
|||
mySuggestions = theSuggestions;
|
||||
}
|
||||
|
||||
public void setFindPhrasesWith() {
|
||||
myPartialMatchPhrases = new ArrayList<String>();
|
||||
myPartialMatchScores = new ArrayList<Float>();
|
||||
|
||||
for (Suggestion next : mySuggestions) {
|
||||
myPartialMatchPhrases.add(' ' + next.myTerm);
|
||||
myPartialMatchScores.add(next.myScore);
|
||||
}
|
||||
|
||||
myPartialMatchPhrases.add(myOriginalSearch);
|
||||
myPartialMatchScores.add(1.0f);
|
||||
}
|
||||
|
||||
public void setAnalyzer(String theString) {
|
||||
myAnalyzer = theString;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String highlightTerm(String theOriginalText, TokenGroup theTokenGroup) {
|
||||
ourLog.debug("{} Found {} with score {}", new Object[] { myAnalyzer, theOriginalText, theTokenGroup.getTotalScore() });
|
||||
ourLog.debug("{} Found {} with score {}", new Object[]{myAnalyzer, theOriginalText, theTokenGroup.getTotalScore()});
|
||||
if (theTokenGroup.getTotalScore() > 0) {
|
||||
float score = theTokenGroup.getTotalScore();
|
||||
if (theOriginalText.equalsIgnoreCase(myOriginalSearch)) {
|
||||
|
@ -379,19 +356,51 @@ public class FulltextSearchSvcImpl extends BaseHapiFhirDao<IBaseResource> implem
|
|||
return null;
|
||||
}
|
||||
|
||||
public void setAnalyzer(String theString) {
|
||||
myAnalyzer = theString;
|
||||
}
|
||||
|
||||
public void setFindPhrasesWith() {
|
||||
myPartialMatchPhrases = new ArrayList<String>();
|
||||
myPartialMatchScores = new ArrayList<Float>();
|
||||
|
||||
for (Suggestion next : mySuggestions) {
|
||||
myPartialMatchPhrases.add(' ' + next.myTerm);
|
||||
myPartialMatchScores.add(next.myScore);
|
||||
}
|
||||
|
||||
myPartialMatchPhrases.add(myOriginalSearch);
|
||||
myPartialMatchScores.add(1.0f);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDisabled() {
|
||||
try {
|
||||
FullTextEntityManager em = org.hibernate.search.jpa.Search.getFullTextEntityManager(myEntityManager);
|
||||
em.getSearchFactory().buildQueryBuilder().forEntity(ResourceTable.class).get();
|
||||
} catch (Exception e) {
|
||||
ourLog.trace("FullText test failed", e);
|
||||
ourLog.debug("Hibernate Search (Lucene) appears to be disabled on this server, fulltext will be disabled");
|
||||
return true;
|
||||
public static class Suggestion implements Comparable<Suggestion> {
|
||||
private String myTerm;
|
||||
private float myScore;
|
||||
|
||||
public Suggestion(String theTerm, float theScore) {
|
||||
myTerm = theTerm;
|
||||
myScore = theScore;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Suggestion theO) {
|
||||
return Float.compare(theO.myScore, myScore);
|
||||
}
|
||||
|
||||
public float getScore() {
|
||||
return myScore;
|
||||
}
|
||||
|
||||
public String getTerm() {
|
||||
return myTerm;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Suggestion[myTerm=" + myTerm + ", myScore=" + myScore + "]";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1378,7 +1378,13 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
throw new InvalidRequestException("Fulltext search is not enabled on this service, can not process parameter: " + Constants.PARAM_CONTENT);
|
||||
}
|
||||
}
|
||||
List<Long> pids = myFulltextSearchSvc.everything(myResourceName, myParams);
|
||||
|
||||
List<Long> pids;
|
||||
if (myParams.getEverythingMode() != null) {
|
||||
pids = myFulltextSearchSvc.everything(myResourceName, myParams);
|
||||
} else {
|
||||
pids = myFulltextSearchSvc.search(myResourceName, myParams);
|
||||
}
|
||||
if (pids.isEmpty()) {
|
||||
// Will never match
|
||||
pids = Collections.singletonList(-1L);
|
||||
|
@ -1576,7 +1582,9 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
cq.where(from.get("myId").in(pids));
|
||||
TypedQuery<ResourceTable> q = entityManager.createQuery(cq);
|
||||
|
||||
for (ResourceTable next : q.getResultList()) {
|
||||
List<ResourceTable> resultList = q.getResultList();
|
||||
|
||||
for (ResourceTable next : resultList) {
|
||||
Class<? extends IBaseResource> resourceType = context.getResourceDefinition(next.getResourceType()).getImplementingClass();
|
||||
IBaseResource resource = theDao.toResource(resourceType, next, theForHistoryOperation);
|
||||
Integer index = position.get(next.getId());
|
||||
|
@ -1615,7 +1623,7 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
// when running asserts
|
||||
assert new HashSet<>(theIncludePids).size() == theIncludePids.size() : "PID list contains duplicates: " + theIncludePids;
|
||||
|
||||
Map<Long, Integer> position = new HashMap<Long, Integer>();
|
||||
Map<Long, Integer> position = new HashMap<>();
|
||||
for (Long next : theIncludePids) {
|
||||
position.put(next, theResourceListToPopulate.size());
|
||||
theResourceListToPopulate.add(null);
|
||||
|
|
|
@ -263,7 +263,7 @@ public class PersistedJpaBundleProvider implements IBundleProvider {
|
|||
}
|
||||
|
||||
// Execute the query and make sure we return distinct results
|
||||
List<IBaseResource> resources = new ArrayList<IBaseResource>();
|
||||
List<IBaseResource> resources = new ArrayList<>();
|
||||
sb.loadResourcesByPid(pidsSubList, resources, includedPids, false, myEntityManager, myContext, myDao);
|
||||
|
||||
return resources;
|
||||
|
|
|
@ -23,6 +23,8 @@ package ca.uhn.fhir.jpa.search;
|
|||
import java.util.List;
|
||||
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.TransactionStatus;
|
||||
import org.springframework.transaction.support.TransactionCallback;
|
||||
|
@ -36,7 +38,7 @@ import ca.uhn.fhir.jpa.search.SearchCoordinatorSvcImpl.SearchTask;
|
|||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
|
||||
public class PersistedJpaSearchFirstPageBundleProvider extends PersistedJpaBundleProvider {
|
||||
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(PersistedJpaSearchFirstPageBundleProvider.class);
|
||||
private SearchTask mySearchTask;
|
||||
private ISearchBuilder mySearchBuilder;
|
||||
private Search mySearch;
|
||||
|
@ -54,20 +56,31 @@ public class PersistedJpaSearchFirstPageBundleProvider extends PersistedJpaBundl
|
|||
@Override
|
||||
public List<IBaseResource> getResources(int theFromIndex, int theToIndex) {
|
||||
SearchCoordinatorSvcImpl.verifySearchHasntFailedOrThrowInternalErrorException(mySearch);
|
||||
|
||||
ourLog.trace("Fetching search resource PIDs");
|
||||
final List<Long> pids = mySearchTask.getResourcePids(theFromIndex, theToIndex);
|
||||
|
||||
ourLog.trace("Done fetching search resource PIDs");
|
||||
|
||||
TransactionTemplate txTemplate = new TransactionTemplate(myTxManager);
|
||||
txTemplate.setPropagationBehavior(TransactionTemplate.PROPAGATION_REQUIRED);
|
||||
return txTemplate.execute(new TransactionCallback<List<IBaseResource>>() {
|
||||
List<IBaseResource> retVal = txTemplate.execute(new TransactionCallback<List<IBaseResource>>() {
|
||||
@Override
|
||||
public List<IBaseResource> doInTransaction(TransactionStatus theStatus) {
|
||||
return toResourceList(mySearchBuilder, pids);
|
||||
}});
|
||||
}
|
||||
});
|
||||
|
||||
ourLog.trace("Loaded resources to return");
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer size() {
|
||||
ourLog.trace("Waiting for initial sync");
|
||||
Integer size = mySearchTask.awaitInitialSync();
|
||||
ourLog.trace("Finished waiting for local sync");
|
||||
|
||||
SearchCoordinatorSvcImpl.verifySearchHasntFailedOrThrowInternalErrorException(mySearch);
|
||||
if (size != null) {
|
||||
return size;
|
||||
|
|
|
@ -117,8 +117,13 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
|
|||
if (myNeverUseLocalSearchForUnitTests == false) {
|
||||
SearchTask task = myIdToSearchTask.get(theUuid);
|
||||
if (task != null) {
|
||||
ourLog.trace("Local search found");
|
||||
return task.getResourcePids(theFrom, theTo);
|
||||
} else {
|
||||
ourLog.trace("No local search found");
|
||||
}
|
||||
} else {
|
||||
ourLog.trace("Forced not using local search");
|
||||
}
|
||||
|
||||
TransactionTemplate txTemplate = new TransactionTemplate(myManagedTxManager);
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
package ca.uhn.fhir.jpa.config;
|
||||
|
||||
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
||||
import ca.uhn.fhir.jpa.search.LuceneSearchMappingFactory;
|
||||
import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor;
|
||||
import ca.uhn.fhir.validation.ResultSeverityEnum;
|
||||
import net.ttddyy.dsproxy.listener.ThreadQueryCountHolder;
|
||||
import net.ttddyy.dsproxy.listener.logging.SLF4JLogLevel;
|
||||
import net.ttddyy.dsproxy.support.ProxyDataSourceBuilder;
|
||||
import org.apache.commons.dbcp2.BasicDataSource;
|
||||
import org.hibernate.jpa.HibernatePersistenceProvider;
|
||||
|
@ -30,6 +28,17 @@ import static org.junit.Assert.*;
|
|||
public class TestR4Config extends BaseJavaConfigR4 {
|
||||
|
||||
static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(TestR4Config.class);
|
||||
private static int ourMaxThreads;
|
||||
|
||||
static {
|
||||
/*
|
||||
* We use a randomized number of maximum threads in order to try
|
||||
* and catch any potential deadlocks caused by database connection
|
||||
* starvation
|
||||
*/
|
||||
ourMaxThreads = (int) (Math.random() * 6.0) + 1;
|
||||
}
|
||||
|
||||
private Exception myLastStackTrace;
|
||||
|
||||
@Bean()
|
||||
|
@ -90,13 +99,7 @@ public class TestR4Config extends BaseJavaConfigR4 {
|
|||
retVal.setUsername("");
|
||||
retVal.setPassword("");
|
||||
|
||||
/*
|
||||
* We use a randomized number of maximum threads in order to try
|
||||
* and catch any potential deadlocks caused by database connection
|
||||
* starvation
|
||||
*/
|
||||
int maxThreads = (int) (Math.random() * 6.0) + 1;
|
||||
retVal.setMaxTotal(maxThreads);
|
||||
retVal.setMaxTotal(ourMaxThreads);
|
||||
|
||||
DataSource dataSource = ProxyDataSourceBuilder
|
||||
.create(retVal)
|
||||
|
@ -155,4 +158,8 @@ public class TestR4Config extends BaseJavaConfigR4 {
|
|||
return retVal;
|
||||
}
|
||||
|
||||
public static int getMaxThreads() {
|
||||
return ourMaxThreads;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package ca.uhn.fhir.jpa.provider.r4;
|
||||
|
||||
import ca.uhn.fhir.jpa.config.TestR4Config;
|
||||
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
||||
import ca.uhn.fhir.jpa.entity.Search;
|
||||
import ca.uhn.fhir.jpa.search.SearchCoordinatorSvcImpl;
|
||||
|
@ -176,7 +177,6 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
|||
return ids;
|
||||
}
|
||||
|
||||
// Y
|
||||
@Test
|
||||
public void testBundleCreate() throws Exception {
|
||||
IGenericClient client = myClient;
|
||||
|
@ -1569,24 +1569,56 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testGetResourceCountsOperation() throws Exception {
|
||||
String methodName = "testMetaOperations";
|
||||
public void testFulltextEverythingWithIdAndContent() throws IOException {
|
||||
Patient p = new Patient();
|
||||
p.setId("FOO");
|
||||
p.addName().setFamily("FAMILY");
|
||||
myClient.update().resource(p).execute();
|
||||
|
||||
Patient pt = new Patient();
|
||||
pt.addName().setFamily(methodName);
|
||||
myClient.create().resource(pt).execute().getId().toUnqualifiedVersionless();
|
||||
p = new Patient();
|
||||
p.setId("BAR");
|
||||
p.addName().setFamily("HELLO");
|
||||
myClient.update().resource(p).execute();
|
||||
|
||||
HttpGet get = new HttpGet(ourServerBase + "/$get-resource-counts");
|
||||
CloseableHttpResponse response = ourHttpClient.execute(get);
|
||||
try {
|
||||
assertEquals(200, response.getStatusLine().getStatusCode());
|
||||
String output = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
IOUtils.closeQuietly(response.getEntity().getContent());
|
||||
ourLog.info(output);
|
||||
assertThat(output, containsString("<parameter><name value=\"Patient\"/><valueInteger value=\""));
|
||||
} finally {
|
||||
response.close();
|
||||
}
|
||||
Observation o = new Observation();
|
||||
o.setId("BAZ");
|
||||
o.getSubject().setReference("Patient/FOO");
|
||||
o.getCode().setText("GOODBYE");
|
||||
myClient.update().resource(o).execute();
|
||||
|
||||
List<String> ids = searchAndReturnUnqualifiedVersionlessIdValues(ourServerBase + "/Patient/FOO/$everything?_content=White");
|
||||
assertThat(ids, contains("Patient/FOO"));
|
||||
|
||||
ids = searchAndReturnUnqualifiedVersionlessIdValues(ourServerBase + "/Patient/FOO/$everything?_content=HELLO");
|
||||
assertThat(ids, contains("Patient/FOO"));
|
||||
|
||||
ids = searchAndReturnUnqualifiedVersionlessIdValues(ourServerBase + "/Patient/FOO/$everything?_content=GOODBYE");
|
||||
assertThat(ids, containsInAnyOrder("Patient/FOO", "Observation/BAZ"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFulltextSearchWithIdAndContent() throws IOException {
|
||||
Patient p = new Patient();
|
||||
p.setId("FOO");
|
||||
p.addName().setFamily("FAMILY");
|
||||
myClient.update().resource(p).execute();
|
||||
|
||||
p = new Patient();
|
||||
p.setId("BAR");
|
||||
p.addName().setFamily("HELLO");
|
||||
myClient.update().resource(p).execute();
|
||||
|
||||
Observation o = new Observation();
|
||||
o.setId("BAZ");
|
||||
o.getSubject().setReference("Patient/FOO");
|
||||
o.getCode().setText("GOODBYE");
|
||||
myClient.update().resource(o).execute();
|
||||
|
||||
List<String> ids = searchAndReturnUnqualifiedVersionlessIdValues(ourServerBase + "/Patient?_id=FOO&_content=family");
|
||||
assertThat(ids, contains("Patient/FOO"));
|
||||
|
||||
ids = searchAndReturnUnqualifiedVersionlessIdValues(ourServerBase + "/Patient?_id=FOO&_content=HELLO");
|
||||
assertThat(ids, empty());
|
||||
}
|
||||
|
||||
// private void delete(String theResourceType, String theParamName, String theParamValue) {
|
||||
|
@ -1614,6 +1646,27 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
|||
// }
|
||||
// }
|
||||
|
||||
@Test
|
||||
public void testGetResourceCountsOperation() throws Exception {
|
||||
String methodName = "testMetaOperations";
|
||||
|
||||
Patient pt = new Patient();
|
||||
pt.addName().setFamily(methodName);
|
||||
myClient.create().resource(pt).execute().getId().toUnqualifiedVersionless();
|
||||
|
||||
HttpGet get = new HttpGet(ourServerBase + "/$get-resource-counts");
|
||||
CloseableHttpResponse response = ourHttpClient.execute(get);
|
||||
try {
|
||||
assertEquals(200, response.getStatusLine().getStatusCode());
|
||||
String output = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
IOUtils.closeQuietly(response.getEntity().getContent());
|
||||
ourLog.info(output);
|
||||
assertThat(output, containsString("<parameter><name value=\"Patient\"/><valueInteger value=\""));
|
||||
} finally {
|
||||
response.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHasParameter() throws Exception {
|
||||
IIdType pid0;
|
||||
|
@ -2049,6 +2102,54 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
|||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseAndEncodeExtensionWithValueWithExtension() throws IOException {
|
||||
String input = "<Patient xmlns=\"http://hl7.org/fhir\">\n" +
|
||||
" <extension url=\"https://purl.org/elab/fhir/network/StructureDefinition/1/BirthWeight\">\n" +
|
||||
" <valueDecimal>\n" +
|
||||
" <extension url=\"http://www.hl7.org/fhir/extension-data-absent-reason.html\">\n" +
|
||||
" <valueCoding>\n" +
|
||||
" <system value=\"http://hl7.org/fhir/ValueSet/birthweight\"/>\n" +
|
||||
" <code value=\"Underweight\"/>\n" +
|
||||
" <userSelected value=\"false\"/>\n" +
|
||||
" </valueCoding>\n" +
|
||||
" </extension>\n" +
|
||||
" </valueDecimal>\n" +
|
||||
" </extension>\n" +
|
||||
" <identifier>\n" +
|
||||
" <system value=\"https://purl.org/elab/fhir/network/StructureDefinition/1/EuroPrevallStudySubjects\"/>\n" +
|
||||
" <value value=\"1\"/>\n" +
|
||||
" </identifier>\n" +
|
||||
" <gender value=\"female\"/>\n" +
|
||||
"</Patient>";
|
||||
|
||||
HttpPost post = new HttpPost(ourServerBase + "/Patient");
|
||||
post.setEntity(new StringEntity(input, ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
|
||||
CloseableHttpResponse response = ourHttpClient.execute(post);
|
||||
IdType id;
|
||||
try {
|
||||
assertEquals(201, response.getStatusLine().getStatusCode());
|
||||
String newIdString = response.getFirstHeader(Constants.HEADER_LOCATION_LC).getValue();
|
||||
assertThat(newIdString, startsWith(ourServerBase + "/Patient/"));
|
||||
id = new IdType(newIdString);
|
||||
} finally {
|
||||
response.close();
|
||||
}
|
||||
|
||||
HttpGet get = new HttpGet(ourServerBase + "/Patient/" + id.getIdPart() + "?_pretty=true");
|
||||
response = ourHttpClient.execute(get);
|
||||
try {
|
||||
String resp = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
ourLog.info(resp);
|
||||
assertEquals(200, response.getStatusLine().getStatusCode());
|
||||
assertThat(resp, containsString("Underweight"));
|
||||
} finally {
|
||||
IOUtils.closeQuietly(response.getEntity().getContent());
|
||||
response.close();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPatchUsingJsonPatch() throws Exception {
|
||||
String methodName = "testPatchUsingJsonPatch";
|
||||
|
@ -3205,14 +3306,19 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
|||
|
||||
ourLog.info(myCapturingInterceptor.getLastResponse().getAllHeaders().toString());
|
||||
assertThat(myCapturingInterceptor.getLastResponse().getHeaders(Constants.HEADER_X_CACHE), Matchers.<String>empty());
|
||||
assertThat(myCapturingInterceptor.getLastResponse().getHeaders(Constants.HEADER_X_CACHE.toLowerCase()),Matchers.<String>empty());
|
||||
assertThat(myCapturingInterceptor.getLastResponse().getHeaders(Constants.HEADER_X_CACHE.toLowerCase()), Matchers.<String>empty());
|
||||
|
||||
String msg = "Total is " + found.getTotalElement().getValue() + " and took " + sw.getMillis() + " millis";
|
||||
|
||||
// If this fails under load, try increasing the throttle above
|
||||
assertEquals(msg,null, found.getTotalElement().getValue());
|
||||
assertEquals(msg, 1, found.getEntry().size());
|
||||
assertThat(msg, sw.getMillis(), lessThan(1000L));
|
||||
// When we've only got one DB connection available, we are forced to wait for the
|
||||
// search to finish before returning
|
||||
if (TestR4Config.getMaxThreads() > 1) {
|
||||
assertEquals(null, found.getTotalElement().getValue());
|
||||
assertEquals(1, found.getEntry().size());
|
||||
assertThat(sw.getMillis(), lessThan(1000L));
|
||||
} else {
|
||||
assertThat(sw.getMillis(), greaterThan(1000L));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -3239,9 +3345,9 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
|||
|
||||
assertThat(sw.getMillis(), not(lessThan(1000L)));
|
||||
|
||||
// If this fails under load, try increasing the throttle above
|
||||
assertEquals(10, found.getTotalElement().getValue().intValue());
|
||||
assertEquals(1, found.getEntry().size());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -3266,12 +3372,17 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
|||
.execute();
|
||||
|
||||
assertThat(myCapturingInterceptor.getLastResponse().getHeaders(Constants.HEADER_X_CACHE), Matchers.<String>empty());
|
||||
assertThat(myCapturingInterceptor.getLastResponse().getHeaders(Constants.HEADER_X_CACHE.toLowerCase()),Matchers.<String>empty());
|
||||
assertThat(myCapturingInterceptor.getLastResponse().getHeaders(Constants.HEADER_X_CACHE.toLowerCase()), Matchers.<String>empty());
|
||||
|
||||
// If this fails under load, try increasing the throttle above
|
||||
assertEquals(null, found.getTotalElement().getValue());
|
||||
assertEquals(1, found.getEntry().size());
|
||||
assertThat(sw.getMillis(), lessThan(1500L));
|
||||
// WHen we've only got one DB connection available, we are forced to wait for the
|
||||
// search to finish before returning
|
||||
if (TestR4Config.getMaxThreads() > 1) {
|
||||
assertEquals(null, found.getTotalElement().getValue());
|
||||
assertEquals(1, found.getEntry().size());
|
||||
assertThat(sw.getMillis(), lessThan(1500L));
|
||||
} else {
|
||||
assertThat(sw.getMillis(), greaterThan(1500L));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -4353,56 +4464,6 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseAndEncodeExtensionWithValueWithExtension() throws IOException {
|
||||
String input = "<Patient xmlns=\"http://hl7.org/fhir\">\n" +
|
||||
" <extension url=\"https://purl.org/elab/fhir/network/StructureDefinition/1/BirthWeight\">\n" +
|
||||
" <valueDecimal>\n" +
|
||||
" <extension url=\"http://www.hl7.org/fhir/extension-data-absent-reason.html\">\n" +
|
||||
" <valueCoding>\n" +
|
||||
" <system value=\"http://hl7.org/fhir/ValueSet/birthweight\"/>\n" +
|
||||
" <code value=\"Underweight\"/>\n" +
|
||||
" <userSelected value=\"false\"/>\n" +
|
||||
" </valueCoding>\n" +
|
||||
" </extension>\n" +
|
||||
" </valueDecimal>\n" +
|
||||
" </extension>\n" +
|
||||
" <identifier>\n" +
|
||||
" <system value=\"https://purl.org/elab/fhir/network/StructureDefinition/1/EuroPrevallStudySubjects\"/>\n" +
|
||||
" <value value=\"1\"/>\n" +
|
||||
" </identifier>\n" +
|
||||
" <gender value=\"female\"/>\n" +
|
||||
"</Patient>";
|
||||
|
||||
HttpPost post = new HttpPost(ourServerBase + "/Patient");
|
||||
post.setEntity(new StringEntity(input, ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
|
||||
CloseableHttpResponse response = ourHttpClient.execute(post);
|
||||
IdType id;
|
||||
try {
|
||||
assertEquals(201, response.getStatusLine().getStatusCode());
|
||||
String newIdString = response.getFirstHeader(Constants.HEADER_LOCATION_LC).getValue();
|
||||
assertThat(newIdString, startsWith(ourServerBase + "/Patient/"));
|
||||
id = new IdType(newIdString);
|
||||
} finally {
|
||||
response.close();
|
||||
}
|
||||
|
||||
HttpGet get = new HttpGet(ourServerBase + "/Patient/" + id.getIdPart() + "?_pretty=true");
|
||||
response = ourHttpClient.execute(get);
|
||||
try {
|
||||
String resp = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
ourLog.info(resp);
|
||||
assertEquals(200, response.getStatusLine().getStatusCode());
|
||||
assertThat(resp, containsString("Underweight"));
|
||||
} finally {
|
||||
IOUtils.closeQuietly(response.getEntity().getContent());
|
||||
response.close();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
public void testValueSetExpandOperation() throws IOException {
|
||||
CodeSystem cs = myFhirCtx.newXmlParser().parseResource(CodeSystem.class, new InputStreamReader(ResourceProviderR4Test.class.getResourceAsStream("/extensional-case-3-cs.xml")));
|
||||
|
|
|
@ -178,7 +178,7 @@ public abstract class BaseResourceReturningMethodBinding extends BaseMethodBindi
|
|||
if (theRequest.getRequestType() == RequestTypeEnum.GET) {
|
||||
boolean first = true;
|
||||
Map<String, String[]> parameters = theRequest.getParameters();
|
||||
for (String nextParamName : new TreeSet<String>(parameters.keySet())) {
|
||||
for (String nextParamName : new TreeSet<>(parameters.keySet())) {
|
||||
for (String nextParamValue : parameters.get(nextParamName)) {
|
||||
if (first) {
|
||||
b.append('?');
|
||||
|
|
|
@ -77,7 +77,6 @@ public class SearchMethodBinding extends BaseResourceReturningMethodBinding {
|
|||
* Check for parameter combinations and names that are invalid
|
||||
*/
|
||||
List<IParameter> parameters = getParameters();
|
||||
// List<SearchParameter> searchParameters = new ArrayList<SearchParameter>();
|
||||
for (int i = 0; i < parameters.size(); i++) {
|
||||
IParameter next = parameters.get(i);
|
||||
if (!(next instanceof SearchParameter)) {
|
||||
|
@ -93,12 +92,7 @@ public class SearchMethodBinding extends BaseResourceReturningMethodBinding {
|
|||
}
|
||||
}
|
||||
|
||||
// searchParameters.add(sp);
|
||||
}
|
||||
// for (int i = 0; i < searchParameters.size(); i++) {
|
||||
// SearchParameter next = searchParameters.get(i);
|
||||
// // next.
|
||||
// }
|
||||
|
||||
/*
|
||||
* Only compartment searching methods may have an ID parameter
|
||||
|
|
|
@ -17,6 +17,10 @@
|
|||
RFC 3986. This affects client calls, as well as URLs generated by
|
||||
the server (e.g. REST HOOK calls). Thanks to James Daily for reporting!
|
||||
</action>
|
||||
<action type="fix">
|
||||
Searching in JPA server using a combination of _content and _id parameters
|
||||
failed. Thanks to Jeff Weyer for reporting!
|
||||
</action>
|
||||
</release>
|
||||
<release version="3.1.0" date="2017-11-23">
|
||||
<action type="add">
|
||||
|
|
Loading…
Reference in New Issue