Perf work

This commit is contained in:
James Agnew 2017-04-20 05:59:10 -04:00
parent 552bd90d59
commit 282f90d174
2 changed files with 230 additions and 117 deletions

View File

@ -24,6 +24,7 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
import java.util.*; import java.util.*;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle; import org.apache.commons.lang3.builder.ToStringStyle;
@ -32,81 +33,28 @@ import ca.uhn.fhir.model.api.IQueryParameterAnd;
import ca.uhn.fhir.model.api.IQueryParameterOr; import ca.uhn.fhir.model.api.IQueryParameterOr;
import ca.uhn.fhir.model.api.IQueryParameterType; import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.model.api.Include; import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.rest.api.SortOrderEnum;
import ca.uhn.fhir.rest.api.SortSpec; import ca.uhn.fhir.rest.api.SortSpec;
import ca.uhn.fhir.rest.param.DateParam; import ca.uhn.fhir.rest.param.DateParam;
import ca.uhn.fhir.rest.param.DateRangeParam; import ca.uhn.fhir.rest.param.DateRangeParam;
import ca.uhn.fhir.rest.param.ParameterUtil;
import ca.uhn.fhir.rest.server.Constants; import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.util.ObjectUtil; import ca.uhn.fhir.util.ObjectUtil;
import ca.uhn.fhir.util.UrlUtil;
public class SearchParameterMap extends LinkedHashMap<String, List<List<? extends IQueryParameterType>>> { public class SearchParameterMap extends LinkedHashMap<String, List<List<? extends IQueryParameterType>>> {
public class QueryParameterTypeComparator implements Comparator<IQueryParameterType> {
private final FhirContext myCtx;
public QueryParameterTypeComparator(FhirContext theCtx) {
myCtx = theCtx;
}
@Override
public int compare(IQueryParameterType theO1, IQueryParameterType theO2) {
FhirContext ctx = myCtx;
int retVal;
if (theO1.getMissing() == null && theO2.getMissing() == null) {
retVal = 0;
} else if (theO1.getMissing() == null) {
retVal = -1;
} else if (theO2.getMissing() == null) {
retVal = 1;
} else if (ObjectUtil.equals(theO1.getMissing(), theO2.getMissing())) {
retVal = 0;
} else {
if (theO1.getMissing().booleanValue()) {
retVal = 1;
} else {
retVal = -1;
}
}
if (retVal == 0) {
String q1 = theO1.getQueryParameterQualifier();
String q2 = theO2.getQueryParameterQualifier();
retVal = StringUtils.compare(q1, q2);
}
if (retVal == 0) {
String v1 = theO1.getValueAsQueryToken(ctx);
}
return retVal;
}
}
public class QueryParameterOrComparator implements Comparator<List<IQueryParameterType>> {
@Override
public int compare(List<IQueryParameterType> theO1, List<IQueryParameterType> theO2) {
// TODO Auto-generated method stub
return 0;
}
}
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private Integer myCount; private Integer myCount;
private EverythingModeEnum myEverythingMode = null; private EverythingModeEnum myEverythingMode = null;
private Set<Include> myIncludes; private Set<Include> myIncludes;
private DateRangeParam myLastUpdated; private DateRangeParam myLastUpdated;
private boolean myLoadSynchronous;
private Integer myLoadSynchronousUpTo; private Integer myLoadSynchronousUpTo;
private Set<Include> myRevIncludes; private Set<Include> myRevIncludes;
private SortSpec mySort; private SortSpec mySort;
private boolean myLoadSynchronous;
/** /**
* Constructor * Constructor
*/ */
@ -121,6 +69,11 @@ public class SearchParameterMap extends LinkedHashMap<String, List<List<? extend
add(theName, theParam); add(theName, theParam);
} }
public SearchParameterMap add(String theName, DateParam theDateParam) {
add(theName, (IQueryParameterOr<?>) theDateParam);
return this;
}
public void add(String theName, IQueryParameterAnd<?> theAnd) { public void add(String theName, IQueryParameterAnd<?> theAnd) {
if (theAnd == null) { if (theAnd == null) {
return; return;
@ -148,11 +101,6 @@ public class SearchParameterMap extends LinkedHashMap<String, List<List<? extend
get(theName).add(theOr.getValuesAsQueryTokens()); get(theName).add(theOr.getValuesAsQueryTokens());
} }
public SearchParameterMap add(String theName, DateParam theDateParam) {
add(theName, (IQueryParameterOr<?>)theDateParam);
return this;
}
public SearchParameterMap add(String theName, IQueryParameterType theParam) { public SearchParameterMap add(String theName, IQueryParameterType theParam) {
assert !Constants.PARAM_LASTUPDATED.equals(theName); // this has it's own field in the map assert !Constants.PARAM_LASTUPDATED.equals(theName); // this has it's own field in the map
@ -214,6 +162,14 @@ public class SearchParameterMap extends LinkedHashMap<String, List<List<? extend
return retVal; return retVal;
} }
/**
* If set, tells the server to load these results synchronously, and not to load
* more than X results
*/
public Integer getLoadSynchronousUpTo() {
return myLoadSynchronousUpTo;
}
public Set<Include> getRevIncludes() { public Set<Include> getRevIncludes() {
if (myRevIncludes == null) { if (myRevIncludes == null) {
myRevIncludes = new HashSet<Include>(); myRevIncludes = new HashSet<Include>();
@ -225,6 +181,14 @@ public class SearchParameterMap extends LinkedHashMap<String, List<List<? extend
return mySort; return mySort;
} }
/**
* If set, tells the server to load these results synchronously, and not to load
* more than X results
*/
public boolean isLoadSynchronous() {
return myLoadSynchronous;
}
public void setCount(Integer theCount) { public void setCount(Integer theCount) {
myCount = theCount; myCount = theCount;
} }
@ -245,8 +209,9 @@ public class SearchParameterMap extends LinkedHashMap<String, List<List<? extend
* If set, tells the server to load these results synchronously, and not to load * If set, tells the server to load these results synchronously, and not to load
* more than X results * more than X results
*/ */
public Integer getLoadSynchronousUpTo() { public SearchParameterMap setLoadSynchronous(boolean theLoadSynchronous) {
return myLoadSynchronousUpTo; myLoadSynchronous = theLoadSynchronous;
return this;
} }
/** /**
@ -262,23 +227,6 @@ public class SearchParameterMap extends LinkedHashMap<String, List<List<? extend
return this; return this;
} }
/**
* If set, tells the server to load these results synchronously, and not to load
* more than X results
*/
public SearchParameterMap setLoadSynchronous(boolean theLoadSynchronous) {
myLoadSynchronous = theLoadSynchronous;
return this;
}
/**
* If set, tells the server to load these results synchronously, and not to load
* more than X results
*/
public boolean isLoadSynchronous() {
return myLoadSynchronous;
}
/** /**
* @deprecated As of HAPI FHIR 2.4 this method no longer does anything * @deprecated As of HAPI FHIR 2.4 this method no longer does anything
*/ */
@ -295,6 +243,140 @@ public class SearchParameterMap extends LinkedHashMap<String, List<List<? extend
mySort = theSort; mySort = theSort;
} }
public String toNormalizedQueryString(FhirContext theCtx) {
StringBuilder b = new StringBuilder();
ArrayList<String> keys = new ArrayList<String>(keySet());
Collections.sort(keys);
for (String nextKey : keys) {
List<List<? extends IQueryParameterType>> nextValuesAndsIn = get(nextKey);
List<List<IQueryParameterType>> nextValuesAndsOut = new ArrayList<List<IQueryParameterType>>();
for (List<? extends IQueryParameterType> nextValuesAndIn : nextValuesAndsIn) {
List<IQueryParameterType> nextValuesOrsOut = new ArrayList<IQueryParameterType>();
for (IQueryParameterType nextValueOrIn : nextValuesAndIn) {
if (nextValueOrIn.getMissing() != null || isNotBlank(nextValueOrIn.getValueAsQueryToken(theCtx))) {
nextValuesOrsOut.add(nextValueOrIn);
}
}
Collections.sort(nextValuesOrsOut, new QueryParameterTypeComparator(theCtx));
if (nextValuesOrsOut.size() > 0) {
nextValuesAndsOut.add(nextValuesOrsOut);
}
} // for AND
Collections.sort(nextValuesAndsOut, new QueryParameterOrComparator(theCtx));
for (List<IQueryParameterType> nextValuesAnd : nextValuesAndsOut) {
addUrlParamSeparator(b);
IQueryParameterType firstValue = nextValuesAnd.get(0);
b.append(UrlUtil.escape(nextKey));
if (firstValue.getMissing() != null) {
b.append(Constants.PARAMQUALIFIER_MISSING);
b.append('=');
if (firstValue.getMissing()) {
b.append(Constants.PARAMQUALIFIER_MISSING_TRUE);
}else {
b.append(Constants.PARAMQUALIFIER_MISSING_FALSE);
}
continue;
}
if (isNotBlank(firstValue.getQueryParameterQualifier())){
b.append(firstValue.getQueryParameterQualifier());
}
b.append('=');
for (int i = 0; i < nextValuesAnd.size(); i++) {
IQueryParameterType nextValueOr = nextValuesAnd.get(i);
if (i > 0) {
b.append(',');
}
b.append(ParameterUtil.escapeAndUrlEncode(nextValueOr.getValueAsQueryToken(theCtx)));
}
}
} // for keys
SortSpec sort = getSort();
boolean first = true;
while (sort != null) {
if (isNotBlank(sort.getParamName())) {
if (first) {
b.append(Constants.PARAM_SORT);
b.append('=');
first = false;
} else {
b.append(',');
}
if (sort.getOrder() == SortOrderEnum.DESC) {
b.append('-');
}
b.append(sort.getParamName());
}
Validate.isTrue(sort != sort.getChain()); // just in case, shouldn't happen
sort = sort.getChain();
}
addUrlIncludeParams(b, Constants.PARAM_INCLUDE, getIncludes());
addUrlIncludeParams(b, Constants.PARAM_REVINCLUDE, getRevIncludes());
if (getLastUpdated() != null) {
DateParam lb = getLastUpdated().getLowerBound();
addLastUpdateParam(b, lb);
DateParam ub = getLastUpdated().getUpperBound();
addLastUpdateParam(b, ub);
}
if (getCount() != null) {
b.append(Constants.PARAM_COUNT);
b.append('=');
b.append(getCount());
}
return b.toString();
}
private void addLastUpdateParam(StringBuilder b, DateParam date) {
if (isNotBlank(date.getValueAsString())) {
b.append(Constants.PARAM_LASTUPDATED);
b.append('=');
b.append(date.getValueAsString());
}
}
private void addUrlIncludeParams(StringBuilder b, String paramName, Set<Include> list) {
for (Include nextInclude : list) {
b.append(paramName);
b.append('=');
b.append(UrlUtil.escape(nextInclude.getParamType()));
b.append(':');
b.append(UrlUtil.escape(nextInclude.getParamName()));
if (isNotBlank(nextInclude.getParamTargetType())) {
b.append(':');
b.append(nextInclude.getParamTargetType());
}
}
}
private void addUrlParamSeparator(StringBuilder theB) {
if (theB.length() == 0) {
theB.append('?');
} else {
theB.append('&');
}
}
@Override @Override
public String toString() { public String toString() {
ToStringBuilder b = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE); ToStringBuilder b = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE);
@ -307,6 +389,38 @@ public class SearchParameterMap extends LinkedHashMap<String, List<List<? extend
return b.toString(); return b.toString();
} }
static int compare(FhirContext theCtx, IQueryParameterType theO1, IQueryParameterType theO2) {
int retVal;
if (theO1.getMissing() == null && theO2.getMissing() == null) {
retVal = 0;
} else if (theO1.getMissing() == null) {
retVal = -1;
} else if (theO2.getMissing() == null) {
retVal = 1;
} else if (ObjectUtil.equals(theO1.getMissing(), theO2.getMissing())) {
retVal = 0;
} else {
if (theO1.getMissing().booleanValue()) {
retVal = 1;
} else {
retVal = -1;
}
}
if (retVal == 0) {
String q1 = theO1.getQueryParameterQualifier();
String q2 = theO2.getQueryParameterQualifier();
retVal = StringUtils.compare(q1, q2);
}
if (retVal == 0) {
String v1 = theO1.getValueAsQueryToken(theCtx);
String v2 = theO2.getValueAsQueryToken(theCtx);
retVal = StringUtils.compare(v1, v2);
}
return retVal;
}
public enum EverythingModeEnum { public enum EverythingModeEnum {
/* /*
* Don't reorder! We rely on the ordinals * Don't reorder! We rely on the ordinals
@ -339,40 +453,34 @@ public class SearchParameterMap extends LinkedHashMap<String, List<List<? extend
} }
} }
public String toQueryString(FhirContext theCtx) { public class QueryParameterOrComparator implements Comparator<List<IQueryParameterType>> {
StringBuilder b = new StringBuilder(); private final FhirContext myCtx;
ArrayList<String> keys = new ArrayList<String>(keySet());
Collections.sort(keys);
for (String nextKey : keys) {
List<List<? extends IQueryParameterType>> nextValuesAndsIn = get(nextKey);
List<List<IQueryParameterType>> nextValuesAndsOut = new ArrayList<List<IQueryParameterType>>();
for (List<? extends IQueryParameterType> nextValuesAndIn : nextValuesAndsIn) {
List<IQueryParameterType> nextValuesOrsOut = new ArrayList<IQueryParameterType>();
for (List<? extends IQueryParameterType> nextValuesOrsIn : nextValuesAndsIn) {
nextValuesOrsIn = new ArrayList<IQueryParameterType>(nextValuesOrsIn);
Collections.sort(nextValuesOrsIn, new QueryParameterTypeComparator());
for (IQueryParameterType nextValueOrIn : nextValuesOrsIn) {
if (nextValueOrIn.getMissing() != null || isNotBlank(nextValueOrIn.getValueAsQueryToken(theCtx))) {
nextValuesOrsOut.add(nextValueOrIn);
}
}
} // for OR
if (nextValuesOrsOut.size() > 0) {
nextValuesAndsOut.add(nextValuesOrsOut);
}
} // for AND
Collections.sort(nextValuesAndsOut, new QueryParameterTypeComparator());
public QueryParameterOrComparator(FhirContext theCtx) {
myCtx = theCtx;
} }
@Override
public int compare(List<IQueryParameterType> theO1, List<IQueryParameterType> theO2) {
// These lists will never be empty
return SearchParameterMap.compare(myCtx, theO1.get(0), theO2.get(0));
}
}
public class QueryParameterTypeComparator implements Comparator<IQueryParameterType> {
private final FhirContext myCtx;
public QueryParameterTypeComparator(FhirContext theCtx) {
myCtx = theCtx;
}
@Override
public int compare(IQueryParameterType theO1, IQueryParameterType theO2) {
return SearchParameterMap.compare(myCtx, theO1, theO2);
}
} }
} }

View File

@ -6,6 +6,7 @@ import static org.junit.Assert.assertEquals;
import org.junit.AfterClass; import org.junit.AfterClass;
import org.junit.Test; import org.junit.Test;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.dao.SearchParameterMap; import ca.uhn.fhir.jpa.dao.SearchParameterMap;
import ca.uhn.fhir.jpa.dao.SearchParameterMap.EverythingModeEnum; import ca.uhn.fhir.jpa.dao.SearchParameterMap.EverythingModeEnum;
import ca.uhn.fhir.rest.param.*; import ca.uhn.fhir.rest.param.*;
@ -18,6 +19,8 @@ public class SearchParameterMapTest {
TestUtil.clearAllStaticFieldsForUnitTest(); TestUtil.clearAllStaticFieldsForUnitTest();
} }
private static FhirContext ourCtx = FhirContext.forDstu3();
@Test @Test
public void testToQueryString() { public void testToQueryString() {
@ -34,9 +37,11 @@ public class SearchParameterMapTest {
.addAnd(new DateOrListParam().add(new DateParam(ParamPrefixEnum.LESSTHAN, "2002"))); .addAnd(new DateOrListParam().add(new DateParam(ParamPrefixEnum.LESSTHAN, "2002")));
map.add("birthdate", birthdateAnd); map.add("birthdate", birthdateAnd);
String queryString = map.toQueryString(); String queryString = map.toNormalizedQueryString(ourCtx);
ourLog.info(queryString);
} }
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchParameterMapTest.class);
/** /**
* {@link Search} uses these ordinals so they shouldn't get out of order * {@link Search} uses these ordinals so they shouldn't get out of order