001/*
002 * Copyright 2020 Global Biodiversity Information Facility (GBIF)
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.gbif.api.model.common.search;
017
018import io.swagger.v3.oas.annotations.Hidden;
019import io.swagger.v3.oas.annotations.Parameter;
020import io.swagger.v3.oas.annotations.enums.Explode;
021import io.swagger.v3.oas.annotations.enums.ParameterIn;
022import io.swagger.v3.oas.annotations.media.ArraySchema;
023import io.swagger.v3.oas.annotations.media.Schema;
024
025import org.gbif.api.model.common.paging.Pageable;
026import org.gbif.api.model.common.paging.PagingRequest;
027
028import java.lang.annotation.Inherited;
029import java.lang.annotation.Retention;
030import java.lang.annotation.RetentionPolicy;
031import java.lang.annotation.Target;
032import java.util.Arrays;
033import java.util.HashMap;
034import java.util.HashSet;
035import java.util.Map;
036import java.util.Set;
037
038import static java.lang.annotation.ElementType.METHOD;
039
040/**
041 * Generic request class for search operations requesting facets.
042 * It extends a search request with a list of desired facets and optional settings.
043 */
044@SuppressWarnings("unused")
045public class FacetedSearchRequest<P extends SearchParameter> extends SearchRequest<P> {
046
047  @Schema(
048    description = "Facet names used to retrieve the most frequent values for fields."
049  )
050  private Set<P> facets = new HashSet<>();
051
052  @Schema(
053    description = "Used in combination with the facet parameter. Set `facetMultiSelect=true` to still return counts for values that are not currently filtered."
054  )
055  private boolean facetMultiSelect;
056
057  @Schema(
058    description = "Used in combination with the facet parameter. Set `facetMincount={#}` to exclude facets with a count less than `{#}`."
059  )
060  private Integer facetMinCount;
061
062  @Schema(
063    description = "Facet parameters allow paging requests using the parameters facetOffset and facetLimit."
064  )
065  private Integer facetLimit = 10;
066
067  @Schema(
068    description = "Facet parameters allow paging requests using the parameters facetOffset and facetLimit."
069  )
070  private Integer facetOffset;
071
072  //Holds the paging configuration for each requested facet
073  @Hidden
074  private Map<P, Pageable> facetPages = new HashMap<>();
075
076  /**
077   * Annotation to document the facet query parameters.
078   */
079  @Target({METHOD})
080  @Retention(RetentionPolicy.RUNTIME)
081  @Inherited
082  @io.swagger.v3.oas.annotations.Parameters(
083    value = {
084      @Parameter(
085        name = "facet",
086        description = "A facet name used to retrieve the most frequent values for a field. This parameter may be " +
087          "repeated to request multiple facets.",
088        array = @ArraySchema(schema = @Schema(implementation = String.class)),
089        in = ParameterIn.QUERY,
090        explode = Explode.TRUE),
091      @Parameter(
092        name = "facetMinCount",
093        description =
094          "Used in combination with the facet parameter. Set `facetMinCount={#}` to exclude facets with a count less than `{#}`.",
095        schema = @Schema(implementation = Integer.class),
096        in = ParameterIn.QUERY),
097      @Parameter(
098        name = "facetMultiselect",
099        description =
100          "Used in combination with the facet parameter. Set `facetMultiselect=true` to still return counts for values that are not currently filtered.",
101        schema = @Schema(implementation = Boolean.class),
102        in = ParameterIn.QUERY),
103      @Parameter(
104        name = "facetLimit",
105        description =
106          "Facet parameters allow paging requests using the parameters facetOffset and facetLimit",
107        schema = @Schema(implementation = Integer.class),
108        in = ParameterIn.QUERY),
109      @Parameter(
110        name = "facetOffset",
111        description =
112          "Facet parameters allow paging requests using the parameters facetOffset and facetLimit",
113        schema = @Schema(implementation = Integer.class, minimum = "0"),
114        in = ParameterIn.QUERY)
115    }
116  )
117  public @interface FacetParameters {}
118
119  public FacetedSearchRequest() {
120  }
121
122  public FacetedSearchRequest(Pageable page) {
123    super(page);
124  }
125
126  public FacetedSearchRequest(SearchRequest<P> searchRequest) {
127    super(searchRequest);
128    setHighlight(searchRequest.isHighlight());
129    setParameters(searchRequest.getParameters());
130    setQ(searchRequest.getQ());
131  }
132
133  public FacetedSearchRequest(long offset, int limit) {
134    super(offset, limit);
135  }
136
137  /**
138   * Min count of facet to return, if the facet count is less than this number the facet won't be included.
139   */
140  public Integer getFacetMinCount() {
141    return facetMinCount;
142  }
143
144  public void setFacetMinCount(Integer facetMinCount) {
145    this.facetMinCount = facetMinCount;
146  }
147
148  /**
149   * Gets the list of requested facets by the search operation.
150   * The facets are a list of search parameters.
151   */
152  public Set<P> getFacets() {
153    return facets;
154  }
155
156  /**
157   * Sets the list of facets.
158   */
159  public void setFacets(Set<P> facets) {
160    this.facets = facets;
161  }
162
163  /**
164   * @return the facetMultiSelect
165   */
166  public boolean isFacetMultiSelect() {
167    return facetMultiSelect;
168  }
169
170  /**
171   * @param facetMultiSelect the facetMultiSelect to set
172   */
173  public void setFacetMultiSelect(boolean facetMultiSelect) {
174    this.facetMultiSelect = facetMultiSelect;
175  }
176
177  /**
178   * Page size of the facet request.
179   */
180  public Integer getFacetLimit() {
181    return facetLimit;
182  }
183
184  public void setFacetLimit(Integer facetLimit) {
185    this.facetLimit = facetLimit;
186  }
187
188  /**
189   * Holds the paging configuration for each requested facet.
190   */
191  public Map<P, Pageable> getFacetPages() {
192    return facetPages;
193  }
194
195  public void setFacetPages(Map<P, Pageable> facetPages) {
196    this.facetPages = facetPages;
197  }
198
199  /**
200   * Sets the paging setting of facet parameter.
201   */
202  public void addFacetPage(P parameter, int facetOffset, int facetLimit){
203    facetPages.put(parameter, new PagingRequest(facetOffset,facetLimit));
204  }
205
206  /**
207   * Gets the paging configuration of a facet parameter.
208   */
209  public Pageable getFacetPage(P parameter) {
210    return facetPages.get(parameter);
211  }
212
213  /**
214   * Offset of the facet request.
215   */
216  public Integer getFacetOffset() {
217    return facetOffset;
218  }
219
220  public void setFacetOffset(Integer facetOffset) {
221    this.facetOffset = facetOffset;
222  }
223
224  public void addFacets(P... facets) {
225    if (this.facets == null) {
226      this.facets = new HashSet<>(Arrays.asList(facets));
227    } else {
228      this.facets.addAll(new HashSet<>(Arrays.asList(facets)));
229    }
230  }
231}