Fix searching in JPA with _id and _content params

This commit is contained in:
jamesagnew 2017-12-05 07:25:34 -05:00
parent e1c62ef03e
commit d710682fed
10 changed files with 294 additions and 193 deletions

View File

@ -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);
@ -91,7 +91,8 @@ public class FulltextSearchSvcImpl extends BaseHapiFhirDao<IBaseResource> implem
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());
}
}
}
}
@ -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) {
@ -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);
}
}
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 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 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;
}
}

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -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);

View File

@ -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;
}
}

View File

@ -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
// 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")));

View File

@ -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('?');

View File

@ -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

View File

@ -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">