001/*
002 * Licensed under the Apache License, Version 2.0 (the "License");
003 * you may not use this file except in compliance with the License.
004 * You may obtain a copy of the License at
005 *
006 *     http://www.apache.org/licenses/LICENSE-2.0
007 *
008 * Unless required by applicable law or agreed to in writing, software
009 * distributed under the License is distributed on an "AS IS" BASIS,
010 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
011 * See the License for the specific language governing permissions and
012 * limitations under the License.
013 */
014package org.gbif.api.model.common.search;
015
016import static org.gbif.api.model.common.paging.PagingConstants.DEFAULT_PARAM_LIMIT;
017import static org.gbif.api.model.common.paging.PagingConstants.DEFAULT_PARAM_OFFSET;
018
019import io.swagger.v3.oas.annotations.Hidden;
020import java.text.DateFormat;
021import java.text.SimpleDateFormat;
022import java.util.Date;
023import java.util.HashMap;
024import java.util.HashSet;
025import java.util.Map;
026import java.util.Optional;
027import java.util.Set;
028import java.util.StringJoiner;
029import lombok.Getter;
030import lombok.Setter;
031import org.gbif.api.annotation.Experimental;
032import org.gbif.api.model.common.paging.Pageable;
033import org.gbif.api.model.common.paging.PageableBase;
034import org.gbif.api.util.IsoDateInterval;
035
036/**
037 * Generic request class for search operations. This class contains a list of parameters, a list of
038 * desired facets and paging options (page size and offset).
039 */
040@SuppressWarnings("unused")
041public abstract class BaseSearchRequest<P extends SearchParameter> extends PageableBase
042    implements SearchRequest<P> {
043
044  @Hidden private Map<P, Set<String>> parameters = new HashMap<>();
045
046  @Hidden private String q;
047
048  @Hidden private boolean highlight;
049
050  @Hidden private boolean spellCheck;
051
052  @Hidden private int spellCheckCount;
053
054  @Hidden private Set<QueryField> qFields = new HashSet<>();
055
056  @Hidden private Set<QueryField> highlightFields = new HashSet<>();
057
058  @Hidden @Experimental @Setter private Boolean matchCase;
059
060  /**
061   * This flag allows to sort the results in a random order by specifying a seed. The seed makes the
062   * random results reproducible so we can use paging. The same seed has to be sent for each page.
063   */
064  @Hidden @Experimental @Getter @Setter private String shuffle;
065
066  /**
067   * This flag enables the use of case-sensitive matches and aggregations on certain search
068   * parameters.
069   *
070   * <p>Fields that support this feature are: occurrenceId, recordedBy, samplingProtocol,
071   * catalogNumber, collectionCode, institutionCode, eventId, parentEventId, waterBody,
072   * stateProvince, recordNumber, identifiedBy, organismId and locality.
073   *
074   * <p>This is an experimental feature, and its implementation may change or be removed at any
075   * time.
076   *
077   * <p>Be aware that this is not a per-field flag, all possible fields will match case sensitively.
078   */
079  @Override
080  @Experimental
081  public Boolean isMatchCase() {
082    return Optional.ofNullable(matchCase).orElse(Boolean.FALSE);
083  }
084
085  /** Constructor with default paging offset & limit. */
086  public BaseSearchRequest() {
087    super(DEFAULT_PARAM_OFFSET, DEFAULT_PARAM_LIMIT);
088  }
089
090  /**
091   * Simple query constructor with default paging offset & limit.
092   *
093   * @param query string for request
094   */
095  public BaseSearchRequest(String query) {
096    super(DEFAULT_PARAM_OFFSET, DEFAULT_PARAM_LIMIT);
097    q = query;
098  }
099
100  /** Minimal paging constructor. */
101  public BaseSearchRequest(Pageable page) {
102    super(page.getOffset(), page.getLimit());
103  }
104
105  /** Minimal paging constructor. */
106  public BaseSearchRequest(long offset, int limit) {
107    super(offset, limit);
108  }
109
110  /**
111   * @return true if highlighted search matches are requested
112   */
113  @Override
114  public boolean isHighlight() {
115    return highlight;
116  }
117
118  /**
119   * @param highlight the highlight to set
120   */
121  @Override
122  public void setHighlight(boolean highlight) {
123    this.highlight = highlight;
124  }
125
126  /**
127   * @return true if spellCheck search is requested
128   */
129  @Override
130  public boolean isSpellCheck() {
131    return spellCheck;
132  }
133
134  /**
135   * @param spellCheck the highlight to set
136   */
137  @Override
138  public void setSpellCheck(boolean spellCheck) {
139    this.spellCheck = spellCheck;
140  }
141
142  /**
143   * @return max number of spell check suggestions requested
144   */
145  @Override
146  public int getSpellCheckCount() {
147    return spellCheckCount;
148  }
149
150  /**
151   * @param spellCheckCount number of spell check suggestions
152   */
153  @Override
154  public void setSpellCheckCount(int spellCheckCount) {
155    this.spellCheckCount = spellCheckCount;
156  }
157
158  /** Defines whether to match against fields with scientific or vernacular names or both. */
159  @Hidden
160  @Override
161  public Set<QueryField> getQFields() {
162    return qFields;
163  }
164
165  @Hidden
166  @Override
167  public void setQFields(Set<QueryField> qFields) {
168    this.qFields = qFields;
169  }
170
171  /** Defines the fields to be highlighted if highlighting is activated. */
172  @Override
173  public Set<QueryField> getHighlightFields() {
174    return highlightFields;
175  }
176
177  @Override
178  public void setHighlightFields(Set<QueryField> highlightFields) {
179    this.highlightFields = highlightFields;
180  }
181
182  /**
183   * List of input parameters of the search operation. The parameters are handled as the parameter
184   * name and the string representation of its value.
185   *
186   * @return the list of parameters
187   */
188  @Override
189  public Map<P, Set<String>> getParameters() {
190    return parameters;
191  }
192
193  /** Sets the list of parameters. */
194  @Override
195  public void setParameters(Map<P, Set<String>> parameters) {
196    this.parameters = parameters;
197  }
198
199  /**
200   * Query parameter.
201   *
202   * @return the q
203   */
204  @Override
205  public String getQ() {
206    return q;
207  }
208
209  /**
210   * @param q the q to set
211   */
212  @Override
213  public void setQ(String q) {
214    this.q = q;
215  }
216
217  /**
218   * Adds the specified parameter.
219   *
220   * @param parameter parameter to add values for
221   * @param values list of values of the parameter to add
222   */
223  @Override
224  public void addParameter(P parameter, Iterable<String> values) {
225    if (parameters.containsKey(parameter)) {
226      Set<String> paramValues = parameters.get(parameter);
227      values.forEach(paramValues::add);
228    } else {
229      Set<String> paramValues = new HashSet<>();
230      values.forEach(paramValues::add);
231      parameters.put(parameter, paramValues);
232    }
233  }
234
235  /**
236   * Adds the specified parameter.
237   *
238   * @param parameter parameter to add values for
239   * @param values list of values of the parameter to add
240   */
241  @Override
242  public void addParameter(P parameter, String... values) {
243    for (String value : values) {
244      addParameter(parameter, value);
245    }
246  }
247
248  /**
249   * Adds the specified parameter.
250   *
251   * @param parameter parameter to add values for
252   * @param value value of the parameter to add
253   */
254  @Override
255  public void addParameter(P parameter, String value) {
256    if (value != null) {
257      if (parameters.containsKey(parameter)) {
258        Set<String> paramValues = parameters.get(parameter);
259        paramValues.add(value);
260      } else {
261        Set<String> paramValues = new HashSet<>();
262        paramValues.add(value);
263        parameters.put(parameter, paramValues);
264      }
265    }
266  }
267
268  /**
269   * Adds the specified long parameter.
270   *
271   * @param parameter parameter to add values for
272   * @param value value of the parameter to add
273   */
274  @Override
275  public void addParameter(P parameter, long value) {
276    addParameter(parameter, String.valueOf(value));
277  }
278
279  /**
280   * Adds the specified int parameter.
281   *
282   * @param parameter parameter to add values for
283   * @param value value of the parameter to add
284   */
285  @Override
286  public void addParameter(P parameter, int value) {
287    addParameter(parameter, String.valueOf(value));
288  }
289
290  /**
291   * Adds the specified double parameter.
292   *
293   * @param parameter parameter to add values for
294   * @param value value of the parameter to add
295   */
296  @Override
297  public void addParameter(P parameter, double value) {
298    addParameter(parameter, String.valueOf(value));
299  }
300
301  /**
302   * Adds the specified boolean parameter.
303   *
304   * @param parameter parameter to add values for
305   * @param value value of the parameter to add
306   */
307  @Override
308  public void addParameter(P parameter, boolean value) {
309    addParameter(parameter, String.valueOf(value));
310  }
311
312  /**
313   * Adds the specified parameter.
314   *
315   * @param parameter parameter to add values for
316   * @param value enum value of the parameter to add
317   */
318  @Override
319  public void addParameter(P parameter, Enum<?> value) {
320    if (value != null) {
321      addParameter(parameter, value.name());
322    }
323  }
324
325  /**
326   * Adds the specified date parameter as an ISO date.
327   *
328   * @param parameter parameter to add date for
329   * @param value date value of the parameter to add
330   */
331  @Override
332  public void addParameter(P parameter, Date value) {
333    if (value != null) {
334      // not thread safe, new instance
335      DateFormat iso = new SimpleDateFormat("yyyy-MM-dd");
336      addParameter(parameter, iso.format(value));
337    }
338  }
339
340  /**
341   * Adds the specified date parameter as an ISO date interval.
342   *
343   * @param parameter parameter to add date interval for
344   * @param value date value of the parameter to add
345   */
346  @Override
347  public void addParameter(P parameter, IsoDateInterval value) {
348    if (value != null) {
349      addParameter(parameter, value.toString());
350    }
351  }
352
353  @Override
354  public void copyPagingValues(Pageable pageable) {
355    super.copyPagingValues(pageable);
356  }
357
358  @Override
359  public String toString() {
360    return new StringJoiner(", ", BaseSearchRequest.class.getSimpleName() + "[", "]")
361        .add("parameters=" + parameters)
362        .add("q='" + q + "'")
363        .add("highlight=" + highlight)
364        .add("spellCheck=" + spellCheck)
365        .add("spellCheckCount=" + spellCheckCount)
366        .add("offset=" + offset)
367        .add("limit=" + limit)
368        .toString();
369  }
370}