Fix offset error when returning multiple pages in JPA search
This commit is contained in:
parent
0093403860
commit
b37a1edaae
|
@ -669,7 +669,9 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
|
||||||
next.processingCompletedNormally(requestDetails);
|
next.processingCompletedNormally(requestDetails);
|
||||||
}
|
}
|
||||||
|
|
||||||
outputStreamOrWriter.close();
|
if (outputStreamOrWriter != null) {
|
||||||
|
outputStreamOrWriter.close();
|
||||||
|
}
|
||||||
|
|
||||||
} catch (NotModifiedException e) {
|
} catch (NotModifiedException e) {
|
||||||
|
|
||||||
|
|
|
@ -256,7 +256,7 @@ public final class PersistedJpaBundleProvider implements IBundleProvider {
|
||||||
return mySearchEntity.getTotalCount();
|
return mySearchEntity.getTotalCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Pageable toPage(int theFromIndex, int theToIndex) {
|
public static Pageable toPage(final int theFromIndex, int theToIndex) {
|
||||||
int pageSize = theToIndex - theFromIndex;
|
int pageSize = theToIndex - theFromIndex;
|
||||||
if (pageSize < 1) {
|
if (pageSize < 1) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -264,7 +264,14 @@ public final class PersistedJpaBundleProvider implements IBundleProvider {
|
||||||
|
|
||||||
int pageIndex = theFromIndex / pageSize;
|
int pageIndex = theFromIndex / pageSize;
|
||||||
|
|
||||||
Pageable page = new PageRequest(pageIndex, pageSize);
|
Pageable page = new PageRequest(pageIndex, pageSize) {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getOffset() {
|
||||||
|
return theFromIndex;
|
||||||
|
}};
|
||||||
|
|
||||||
return page;
|
return page;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,10 +42,7 @@ import org.apache.http.entity.ContentType;
|
||||||
import org.apache.http.entity.StringEntity;
|
import org.apache.http.entity.StringEntity;
|
||||||
import org.apache.http.message.BasicNameValuePair;
|
import org.apache.http.message.BasicNameValuePair;
|
||||||
import org.hl7.fhir.dstu3.model.*;
|
import org.hl7.fhir.dstu3.model.*;
|
||||||
import org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent;
|
import org.hl7.fhir.dstu3.model.Bundle.*;
|
||||||
import org.hl7.fhir.dstu3.model.Bundle.BundleType;
|
|
||||||
import org.hl7.fhir.dstu3.model.Bundle.HTTPVerb;
|
|
||||||
import org.hl7.fhir.dstu3.model.Bundle.SearchEntryMode;
|
|
||||||
import org.hl7.fhir.dstu3.model.Encounter.EncounterLocationComponent;
|
import org.hl7.fhir.dstu3.model.Encounter.EncounterLocationComponent;
|
||||||
import org.hl7.fhir.dstu3.model.Encounter.EncounterStatus;
|
import org.hl7.fhir.dstu3.model.Encounter.EncounterStatus;
|
||||||
import org.hl7.fhir.dstu3.model.Enumerations.AdministrativeGender;
|
import org.hl7.fhir.dstu3.model.Enumerations.AdministrativeGender;
|
||||||
|
@ -74,6 +71,7 @@ import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||||
import ca.uhn.fhir.rest.api.SummaryEnum;
|
import ca.uhn.fhir.rest.api.SummaryEnum;
|
||||||
import ca.uhn.fhir.rest.api.ValidationModeEnum;
|
import ca.uhn.fhir.rest.api.ValidationModeEnum;
|
||||||
import ca.uhn.fhir.rest.client.IGenericClient;
|
import ca.uhn.fhir.rest.client.IGenericClient;
|
||||||
|
import ca.uhn.fhir.rest.gclient.StringClientParam;
|
||||||
import ca.uhn.fhir.rest.param.*;
|
import ca.uhn.fhir.rest.param.*;
|
||||||
import ca.uhn.fhir.rest.server.Constants;
|
import ca.uhn.fhir.rest.server.Constants;
|
||||||
import ca.uhn.fhir.rest.server.IBundleProvider;
|
import ca.uhn.fhir.rest.server.IBundleProvider;
|
||||||
|
@ -146,8 +144,6 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Per message from David Hay on Skype
|
* Per message from David Hay on Skype
|
||||||
*/
|
*/
|
||||||
|
@ -166,14 +162,8 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
|
||||||
|
|
||||||
mySystemDao.transaction(mySrd, inputBundle);
|
mySystemDao.transaction(mySrd, inputBundle);
|
||||||
|
|
||||||
Bundle responseBundle = ourClient
|
Bundle responseBundle = ourClient.operation().onInstance(new IdType("Patient/A161443")).named("everything").withParameter(Parameters.class, "_count", new IntegerType(50)).useHttpGet()
|
||||||
.operation()
|
.returnResourceType(Bundle.class).execute();
|
||||||
.onInstance(new IdType("Patient/A161443"))
|
|
||||||
.named("everything")
|
|
||||||
.withParameter(Parameters.class, "_count", new IntegerType(50))
|
|
||||||
.useHttpGet()
|
|
||||||
.returnResourceType(Bundle.class)
|
|
||||||
.execute();
|
|
||||||
|
|
||||||
TreeSet<String> ids = new TreeSet<String>();
|
TreeSet<String> ids = new TreeSet<String>();
|
||||||
for (int i = 0; i < responseBundle.getEntry().size(); i++) {
|
for (int i = 0; i < responseBundle.getEntry().size(); i++) {
|
||||||
|
@ -202,6 +192,70 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Per message from David Hay on Skype
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testEverythingWithLargeSet2() throws Exception {
|
||||||
|
Patient p = new Patient();
|
||||||
|
p.setActive(true);
|
||||||
|
IIdType id = ourClient.create().resource(p).execute().getId().toUnqualifiedVersionless();
|
||||||
|
|
||||||
|
for (int i = 1; i < 77; i++) {
|
||||||
|
Observation obs = new Observation();
|
||||||
|
obs.setId("A" + StringUtils.leftPad(Integer.toString(i), 2, '0'));
|
||||||
|
obs.setSubject(new Reference(id));
|
||||||
|
ourClient.update().resource(obs).execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
Bundle responseBundle = ourClient.operation().onInstance(id).named("everything").withParameter(Parameters.class, "_count", new IntegerType(50)).useHttpGet().returnResourceType(Bundle.class)
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
TreeSet<String> ids = new TreeSet<String>();
|
||||||
|
for (int i = 0; i < responseBundle.getEntry().size(); i++) {
|
||||||
|
for (BundleEntryComponent nextEntry : responseBundle.getEntry()) {
|
||||||
|
ids.add(nextEntry.getResource().getIdElement().getIdPart());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ourLog.info("Have {} IDs: {}", ids.size(), ids);
|
||||||
|
|
||||||
|
BundleLinkComponent nextLink = responseBundle.getLink("next");
|
||||||
|
while (nextLink != null) {
|
||||||
|
String nextUrl = nextLink.getUrl();
|
||||||
|
responseBundle = ourClient.fetchResourceFromUrl(Bundle.class, nextUrl);
|
||||||
|
for (int i = 0; i < responseBundle.getEntry().size(); i++) {
|
||||||
|
for (BundleEntryComponent nextEntry : responseBundle.getEntry()) {
|
||||||
|
ids.add(nextEntry.getResource().getIdElement().getIdPart());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ourLog.info("Have {} IDs: {}", ids.size(), ids);
|
||||||
|
nextLink = responseBundle.getLink("next");
|
||||||
|
}
|
||||||
|
|
||||||
|
assertThat(ids, hasItem(id.getIdPart()));
|
||||||
|
for (int i = 1; i < 77; i++) {
|
||||||
|
assertThat(ids, hasItem("A" + StringUtils.leftPad(Integer.toString(i), 2, '0')));
|
||||||
|
}
|
||||||
|
assertEquals(77, ids.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEmptySearch() throws Exception {
|
||||||
|
Bundle responseBundle;
|
||||||
|
|
||||||
|
responseBundle = ourClient.search().forResource(Patient.class).returnBundle(Bundle.class).execute();
|
||||||
|
assertEquals(0, responseBundle.getTotal());
|
||||||
|
|
||||||
|
responseBundle = ourClient.search().forResource(Patient.class).where(Patient.NAME.matches().value("AAA")).returnBundle(Bundle.class).execute();
|
||||||
|
assertEquals(0, responseBundle.getTotal());
|
||||||
|
|
||||||
|
responseBundle = ourClient.search().forResource(Patient.class).where(new StringClientParam("_content").matches().value("AAA")).returnBundle(Bundle.class).execute();
|
||||||
|
assertEquals(0, responseBundle.getTotal());
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* See #411
|
* See #411
|
||||||
|
@ -215,8 +269,8 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
|
||||||
patient.addName().addFamily("testSearchWithMixedParams").addGiven("Joe");
|
patient.addName().addFamily("testSearchWithMixedParams").addGiven("Joe");
|
||||||
myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless();
|
myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless();
|
||||||
|
|
||||||
HttpPost httpPost = new HttpPost(ourServerBase + "/Patient/_search?_format=application/xml");
|
HttpPost httpPost = new HttpPost(ourServerBase + "/Patient/_search?_format=application/xml");
|
||||||
httpPost.addHeader("Cache-Control","no-cache");
|
httpPost.addHeader("Cache-Control", "no-cache");
|
||||||
List<NameValuePair> parameters = Lists.newArrayList();
|
List<NameValuePair> parameters = Lists.newArrayList();
|
||||||
parameters.add(new BasicNameValuePair("name", "Smith"));
|
parameters.add(new BasicNameValuePair("name", "Smith"));
|
||||||
httpPost.setEntity(new UrlEncodedFormEntity(parameters));
|
httpPost.setEntity(new UrlEncodedFormEntity(parameters));
|
||||||
|
@ -234,7 +288,6 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSearchPagingKeepsOldSearches() throws Exception {
|
public void testSearchPagingKeepsOldSearches() throws Exception {
|
||||||
String methodName = "testSearchPagingKeepsOldSearches";
|
String methodName = "testSearchPagingKeepsOldSearches";
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
package ca.uhn.fhir.jpa.search;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.springframework.data.domain.Pageable;
|
||||||
|
|
||||||
|
public class PersistedJpaBundleProviderTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetPage() {
|
||||||
|
Pageable page = PersistedJpaBundleProvider.toPage(50, 73);
|
||||||
|
assertEquals(50, page.getOffset());
|
||||||
|
// assertEquals(50, page.get);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -10,11 +10,11 @@
|
||||||
</encoder>
|
</encoder>
|
||||||
</appender>
|
</appender>
|
||||||
|
|
||||||
<logger name="org.springframework.web.socket.handler.ExceptionWebSocketHandlerDecorator" additivity="false" level="debug">
|
<logger name="org.springframework.web.socket.handler.ExceptionWebSocketHandlerDecorator" additivity="false" level="info">
|
||||||
<appender-ref ref="STDOUT" />
|
<appender-ref ref="STDOUT" />
|
||||||
</logger>
|
</logger>
|
||||||
|
|
||||||
<logger name="ca.uhn.fhir.jpa.dao.FhirResourceDaoSubscriptionDstu2" additivity="false" level="debug">
|
<logger name="ca.uhn.fhir.jpa.dao.FhirResourceDaoSubscriptionDstu2" additivity="false" level="info">
|
||||||
<appender-ref ref="STDOUT" />
|
<appender-ref ref="STDOUT" />
|
||||||
</logger>
|
</logger>
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@
|
||||||
</logger>
|
</logger>
|
||||||
|
|
||||||
<!-- Set to 'trace' to enable SQL logging -->
|
<!-- Set to 'trace' to enable SQL logging -->
|
||||||
<logger name="org.hibernate.SQL" additivity="false" level="trace">
|
<logger name="org.hibernate.SQL" additivity="false" level="info">
|
||||||
<appender-ref ref="STDOUT" />
|
<appender-ref ref="STDOUT" />
|
||||||
</logger>
|
</logger>
|
||||||
<!-- Set to 'trace' to enable SQL Value logging -->
|
<!-- Set to 'trace' to enable SQL Value logging -->
|
||||||
|
|
|
@ -28,7 +28,7 @@ public class UhnFhirTestApp {
|
||||||
System.setProperty("fhir.db.location", "./target/testdb");
|
System.setProperty("fhir.db.location", "./target/testdb");
|
||||||
System.setProperty("fhir.db.location.dstu2", "./target/testdb_dstu2");
|
System.setProperty("fhir.db.location.dstu2", "./target/testdb_dstu2");
|
||||||
System.setProperty("fhir.lucene.location.dstu2", "./target/testlucene_dstu2");
|
System.setProperty("fhir.lucene.location.dstu2", "./target/testlucene_dstu2");
|
||||||
System.setProperty("fhir.db.location.dstu3", "./target/testdb_dstu3");
|
System.setProperty("fhir.db.location.dstu3", "./target/fhirtest_dstu3");
|
||||||
System.setProperty("fhir.lucene.location.dstu3", "./target/testlucene_dstu3");
|
System.setProperty("fhir.lucene.location.dstu3", "./target/testlucene_dstu3");
|
||||||
System.setProperty("fhir.db.location.tdl2", "./target/testdb_tdl2");
|
System.setProperty("fhir.db.location.tdl2", "./target/testdb_tdl2");
|
||||||
System.setProperty("fhir.lucene.location.tdl2", "./target/testlucene_tdl2");
|
System.setProperty("fhir.lucene.location.tdl2", "./target/testlucene_tdl2");
|
||||||
|
|
|
@ -16,6 +16,11 @@
|
||||||
</ul>
|
</ul>
|
||||||
]]>
|
]]>
|
||||||
</action>
|
</action>
|
||||||
|
<action type="fix">
|
||||||
|
Fix a fairly significant issue in JPA Server when using the <![CDATA[<code>DatabaseBackedPagingProvider</code>]]>: When paging over the results
|
||||||
|
of a search / $everything operation, under certain circumstances resources may be missing from the last page of results
|
||||||
|
that is returned. Thanks to David Hay for reporting!
|
||||||
|
</action>
|
||||||
<action type="add">
|
<action type="add">
|
||||||
Client, Server, and JPA server now support experimental support
|
Client, Server, and JPA server now support experimental support
|
||||||
for
|
for
|
||||||
|
|
Loading…
Reference in New Issue