001/*- 002 * #%L 003 * Smile CDR - CDR 004 * %% 005 * Copyright (C) 2016 - 2025 Smile CDR, Inc. 006 * %% 007 * All rights reserved. 008 * #L% 009 */ 010package ca.cdr.api.fhirgw.model; 011 012import ca.uhn.fhir.model.api.Include; 013import ca.uhn.fhir.rest.api.SearchStyleEnum; 014import ca.uhn.fhir.rest.api.SearchTotalModeEnum; 015import ca.uhn.fhir.rest.api.SortSpec; 016import ca.uhn.fhir.rest.param.DateRangeParam; 017import ca.uhn.fhir.rest.param.UriAndListParam; 018import ca.uhn.fhir.rest.param.UriOrListParam; 019import com.google.common.collect.ArrayListMultimap; 020import com.google.common.collect.ListMultimap; 021import com.google.common.collect.Multimap; 022import com.google.common.collect.Multimaps; 023import jakarta.annotation.Nonnull; 024import jakarta.annotation.Nullable; 025import org.apache.commons.lang3.Validate; 026 027import java.util.Collections; 028import java.util.HashSet; 029import java.util.List; 030import java.util.Map; 031import java.util.Set; 032 033import static org.apache.commons.lang3.ObjectUtils.getIfNull; 034 035/** 036 * This class represents a FHIR Gateway Search request 037 */ 038public class SearchRequest extends BaseRequest<SearchRequest> { 039 040 private ListMultimap<String, String> myParameters = EMPTY_STRING_MULTIMAP; 041 private Set<Include> myIncludes = Collections.emptySet(); 042 private Set<Include> myRevIncludes = Collections.emptySet(); 043 private SortSpec mySort; 044 private Integer myCount; 045 private SearchTotalModeEnum myTotalMode; 046 private Integer myOffset; 047 private DateRangeParam myLastUpdated; 048 private UriAndListParam myProfile; 049 050 @Nullable 051 private SearchStyleEnum mySearchStyle; 052 053 /** 054 * Constructor 055 */ 056 public SearchRequest() { 057 // nothing 058 } 059 060 /** 061 * Copy constructor 062 */ 063 public SearchRequest(SearchRequest theSearchRequest) { 064 super(theSearchRequest); 065 066 if (!theSearchRequest.getParameters().isEmpty()) { 067 myParameters = ArrayListMultimap.create(); 068 myParameters.putAll(theSearchRequest.getParameters()); 069 } 070 071 if (!theSearchRequest.getIncludes().isEmpty()) { 072 myIncludes = new HashSet<>(); 073 for (Include next : theSearchRequest.getIncludes()) { 074 myIncludes.add(next.toLocked()); 075 } 076 } 077 078 if (!theSearchRequest.getRevIncludes().isEmpty()) { 079 myRevIncludes = new HashSet<>(); 080 for (Include next : theSearchRequest.getRevIncludes()) { 081 myRevIncludes.add(next.toLocked()); 082 } 083 } 084 085 if (theSearchRequest.getSort() != null) { 086 mySort = clone(theSearchRequest.getSort()); 087 } 088 089 if (theSearchRequest.getLastUpdated() != null) { 090 myLastUpdated = new DateRangeParam( 091 theSearchRequest.getLastUpdated().getLowerBound() != null 092 ? theSearchRequest.getLastUpdated().getLowerBound().getValueAsString() 093 : null, 094 theSearchRequest.getLastUpdated().getUpperBound() != null 095 ? theSearchRequest.getLastUpdated().getUpperBound().getValueAsString() 096 : null); 097 } 098 099 if (theSearchRequest.getProfile() != null) { 100 myProfile = new UriAndListParam(); 101 for (UriOrListParam uriOrListParam : theSearchRequest.getProfile().getValuesAsQueryTokens()) { 102 myProfile.addAnd(uriOrListParam); 103 } 104 } 105 106 myCount = theSearchRequest.getCount(); 107 myOffset = theSearchRequest.getOffset(); 108 myTotalMode = theSearchRequest.getTotalMode(); 109 mySearchStyle = theSearchRequest.getSearchStyle(); 110 } 111 112 private static SortSpec clone(SortSpec theSort) { 113 if (theSort == null) { 114 return null; 115 } 116 return new SortSpec(theSort.getParamName(), theSort.getOrder(), clone(theSort.getChain())); 117 } 118 119 /** 120 * Specifies the search form (GET vs POST) to use for this search. If set to 121 * <code>null</code>, the default will be used. 122 */ 123 @Nullable 124 public SearchStyleEnum getSearchStyle() { 125 return mySearchStyle; 126 } 127 128 /** 129 * Specifies the search form (GET vs POST) to use for this search. If set to 130 * <code>null</code>, the default will be used. 131 */ 132 public void setSearchStyle(@Nullable SearchStyleEnum theSearchStyle) { 133 mySearchStyle = theSearchStyle; 134 } 135 136 public Multimap<String, String> getParameters() { 137 return Multimaps.unmodifiableMultimap(myParameters); 138 } 139 140 public SearchRequest setParameters(Map<String, List<String>> theParameters) { 141 Validate.notNull(theParameters, "theParameters must not be null"); 142 myParameters = ArrayListMultimap.create(); 143 for (Map.Entry<String, List<String>> next : theParameters.entrySet()) { 144 myParameters.putAll(next.getKey(), next.getValue()); 145 } 146 return this; 147 } 148 149 /** 150 * Add a new search parameter 151 */ 152 public void addParameter(String theName, String theValue) { 153 Validate.notBlank(theName, "theName must not be empty"); 154 Validate.notBlank(theValue, "theValue must not be empty"); 155 if (myParameters.isEmpty()) { 156 myParameters = ArrayListMultimap.create(); 157 } 158 myParameters.put(theName, theValue); 159 } 160 161 /** 162 * Adds OR parameters to the list of search parameters as a key value 163 * pair where the value is a comma-delimited list i.e. _id=123,345 164 * 165 * @param theName The parameter name 166 * @param theValues the values for the parameter 167 */ 168 public void addOrListParameter(String theName, List<String> theValues) { 169 Validate.notBlank(theName, "theName must not be empty"); 170 Validate.notEmpty(theValues, "theValues must not be empty"); 171 String values = String.join(",", theValues); 172 if (myParameters.isEmpty()) { 173 myParameters = ArrayListMultimap.create(); 174 } 175 myParameters.put(theName, values); 176 } 177 178 /** 179 * Remove all parameters with the given name 180 * 181 * @return Returns any parameter values that were removed 182 */ 183 public List<String> removeParameters(String theName) { 184 if (!myParameters.isEmpty()) { 185 return Collections.unmodifiableList(myParameters.removeAll(theName)); 186 } else { 187 return Collections.emptyList(); 188 } 189 } 190 191 /** 192 * Remove the parameter with the given name and value, if found. Any other parameters with the same name are kept. 193 * 194 * @param theName The parameter name 195 * @param theValue The parameter value 196 * @return Returns <code>true</code> if a parameter value was removed 197 */ 198 public boolean removeParameter(String theName, String theValue) { 199 return myParameters.remove(theName, theValue); 200 } 201 202 /** 203 * Returns the first parameter value for a given parameter name (returns <code>null</code> if no parameters match the given name) 204 * 205 * @param theName The parameter name 206 * @return The value, or <code>null</code> 207 */ 208 @Nullable 209 public String getParameter(String theName) { 210 List<String> values = getParameters(theName); 211 if (!values.isEmpty()) { 212 return values.get(0); 213 } 214 return null; 215 } 216 217 /** 218 * Returns a List of parameter values for the given name. Will not return <code>null</code>. 219 * 220 * @param theName The parameter name 221 * @return A List of values, or an empty List 222 */ 223 @Nonnull 224 public List<String> getParameters(String theName) { 225 List<String> values = myParameters.get(theName); 226 values = getIfNull(values, Collections.emptyList()); 227 return Collections.unmodifiableList(values); 228 } 229 230 public Set<Include> getIncludes() { 231 return myIncludes; 232 } 233 234 public SearchRequest setIncludes(Set<Include> theIncludes) { 235 Validate.notNull(theIncludes, "theIncludes must not be null"); 236 myIncludes = theIncludes; 237 return this; 238 } 239 240 public Set<Include> getRevIncludes() { 241 return myRevIncludes; 242 } 243 244 public SearchRequest setRevIncludes(Set<Include> theRevIncludes) { 245 Validate.notNull(theRevIncludes, "theRevIncludes must not be null"); 246 myRevIncludes = theRevIncludes; 247 return this; 248 } 249 250 public SortSpec getSort() { 251 return mySort; 252 } 253 254 public SearchRequest setSort(SortSpec theSort) { 255 mySort = theSort; 256 return this; 257 } 258 259 public Integer getCount() { 260 return myCount; 261 } 262 263 public SearchRequest setCount(Integer theCount) { 264 myCount = theCount; 265 return this; 266 } 267 268 public Integer getOffset() { 269 return myOffset; 270 } 271 272 public SearchRequest setOffset(Integer theOffset) { 273 myOffset = theOffset; 274 return this; 275 } 276 277 public DateRangeParam getLastUpdated() { 278 return myLastUpdated; 279 } 280 281 public SearchRequest setLastUpdated(DateRangeParam theLastUpdated) { 282 myLastUpdated = theLastUpdated; 283 return this; 284 } 285 286 public UriAndListParam getProfile() { 287 return myProfile; 288 } 289 290 public SearchRequest setProfile(UriAndListParam theProfile) { 291 myProfile = theProfile; 292 return this; 293 } 294 295 public SearchTotalModeEnum getTotalMode() { 296 return myTotalMode; 297 } 298 299 public SearchRequest setTotalMode(SearchTotalModeEnum theTotalMode) { 300 myTotalMode = theTotalMode; 301 return this; 302 } 303}