1 package com
.google
.appengine
.api
.search
;
3 import com
.google
.appengine
.api
.search
.checkers
.FacetChecker
;
4 import com
.google
.appengine
.api
.search
.checkers
.FacetQueryChecker
;
5 import com
.google
.appengine
.api
.search
.checkers
.Preconditions
;
6 import com
.google
.appengine
.api
.search
.checkers
.SearchApiLimits
;
7 import com
.google
.common
.collect
.ImmutableList
;
9 import java
.util
.ArrayList
;
10 import java
.util
.List
;
13 * A facet request representing parameters for requesting specific facets to be returned with a
16 * For example, to request a facet with a name and specific values:
18 * FacetRequest request = FacetRequest.newBuilder().setName("wine_type")
19 * .addValueConstraint("white").addValueConstraint("red").build();
21 * and to request ranges:
23 * FacetRequest request = FacetRequest.newBuilder().setName("year")
24 * .addRange(null, 2000.0) // year < 2000.0
25 * .addRange(1980.0, 2000.0) // 1980.0 <= year < 2000.0
26 * .addRange(2000.0, null).build(); // year >= 2000.0
29 public final class FacetRequest
{
32 * A facet request builder. Each facet request should at least have the {@code name}
33 * of the facet. It can also includes number of values, a list of constraints
34 * on the values or a list of ranges for numeric facets. Note that the list of constraints and
35 * the list of ranges are mutually exclusive, i.e. you can specify one of them but not both.
37 public static final class Builder
{
40 private List
<String
> constraints
= new ArrayList
<>();
41 private List
<FacetRange
> ranges
= new ArrayList
<>();
43 private Integer valueLimit
;
45 private Builder(FacetRequest request
) {
46 this.name
= request
.name
;
47 this.constraints
= new ArrayList
<>(request
.getValueConstraints());
48 this.ranges
= new ArrayList
<>(request
.getRanges());
49 this.valueLimit
= request
.getValueLimit();
56 * Sets the maximum number of values for this facet to return.
58 * @return this Builder
59 * @throws IllegalArgumentException if valueLimit is negetive or zero or greater than
60 * {@link SearchApiLimits#FACET_MAXIMUM_VALUE_LIMIT}
62 public Builder
setValueLimit(int valueLimit
) {
63 this.valueLimit
= FacetQueryChecker
.checkValueLimit(valueLimit
);
68 * Sets the name of the facet for this request.
70 * @return this Builder
71 * @throws IllegalArgumentException if name is empty or longer than
72 * {@link SearchApiLimits#MAXIMUM_NAME_LENGTH}
74 public Builder
setName(String name
) {
75 this.name
= FacetChecker
.checkFacetName(name
);
80 * Adds a value {@code constraint} to this facet request.
81 * Note that ranges and value constraints are mutually exclusive. Either of
82 * them can be provided, but not both for the same request.
84 * @return this Builder
85 * @throws IllegalArgumentException if the constraint empty or longer
86 * than {@link SearchApiLimits#FACET_MAXIMUM_VALUE_LENGTH}.
87 * @throws IllegalStateException if any number of ranges or
88 * {@link SearchApiLimits.FACET_MAXIMUM_CONSTRAINTS} constraints have already been added.
90 public Builder
addValueConstraint(String constraint
) {
91 FacetQueryChecker
.checkFacetValue(constraint
);
92 Preconditions
.checkState(ranges
.isEmpty(), "Ranges list should be empty.");
93 Preconditions
.checkState(constraints
.size() < SearchApiLimits
.FACET_MAXIMUM_CONSTRAINTS
,
94 "More than %d constraints.", SearchApiLimits
.FACET_MAXIMUM_CONSTRAINTS
);
95 constraints
.add(constraint
);
100 * Adds a {@link FacetRange} to this request.
101 * Note that ranges and value constraints are mutually exclusive. Either of
102 * them can be provided, but not both for the same request.
104 * @throws NullPointerException if {@code range} is null.
105 * @throws IllegalStateException if constraints list is not empty or number of ranges became
106 * greater than SearchApiLimits.FACET_MAXIMUM_RANGES by adding this range.
107 * @return this Builder
109 public Builder
addRange(FacetRange range
) {
110 Preconditions
.checkNotNull(range
, "range should not be null.");
111 Preconditions
.checkState(constraints
.isEmpty(), "Constraints list should be empty.");
112 Preconditions
.checkState(ranges
.size() < SearchApiLimits
.FACET_MAXIMUM_RANGES
,
113 "More than %d ranges.", SearchApiLimits
.FACET_MAXIMUM_RANGES
);
119 * Construct the final message.
121 * @return the FacetRequest built from the parameters entered on this
123 * @throws IllegalArgumentException if the facet request is invalid
125 public FacetRequest
build() {
126 return new FacetRequest(this);
130 private final String name
;
132 private final ImmutableList
<String
> constraints
;
133 private final ImmutableList
<FacetRange
> ranges
;
135 private Integer valueLimit
;
137 private FacetRequest(Builder builder
) {
138 this.name
= builder
.name
;
139 this.constraints
= ImmutableList
.copyOf(builder
.constraints
);
140 this.ranges
= ImmutableList
.copyOf(builder
.ranges
);
141 this.valueLimit
= builder
.valueLimit
;
146 * Creates and returns a {@link FacetRequest} builder. Set the facet request
147 * parameters and use the {@link Builder#build()} method to create a concrete
148 * instance of FacetRequest.
150 * @return a {@link Builder} which can construct a facet request
152 public static Builder
newBuilder() {
153 return new Builder();
157 * Creates a builder from the given FacetRequest.
159 * @param request the facet request for the builder to use
160 * to build another request.
161 * @return a new builder with values set from the given request
163 public static Builder
newBuilder(FacetRequest request
) {
164 return new Builder(request
);
168 * Returns the name of the face in this request.
171 public String
getName() {
176 * Returns the maximum number of values this facet should have. Null if the value limit
180 public Integer
getValueLimit() {
185 * Returns an unmodifiable list of {@link FacetRange}s.
188 public List
<FacetRange
> getRanges() {
193 * Returns an unmodifiable list of value constraints.
196 public List
<String
> getValueConstraints() {
201 * Checks the facet request is valid, specifically, has
202 * a non-null non-empty name, non-overlapping ranges and
203 * exclusive ranges or constraints, not both at the same time.
205 * @throws IllegalArgumentException if some part of the specification is
208 private void checkValid() {
209 FacetChecker
.checkFacetName(getName());
210 Preconditions
.checkState(constraints
.size() < SearchApiLimits
.FACET_MAXIMUM_CONSTRAINTS
,
211 "More than %d constraints.", SearchApiLimits
.FACET_MAXIMUM_CONSTRAINTS
);
212 Preconditions
.checkState(ranges
.size() < SearchApiLimits
.FACET_MAXIMUM_RANGES
,
213 "More than %d ranges.", SearchApiLimits
.FACET_MAXIMUM_RANGES
);
214 Preconditions
.checkState(constraints
.isEmpty() || ranges
.isEmpty(),
215 "Constraints and ranges set for the same request.");
216 for (String constraint
: getValueConstraints()) {
217 FacetQueryChecker
.checkFacetValue(constraint
);
222 * Copies the contents of this {@link FacetRequest} object into a
223 * {@link SearchServicePb.FacetRequest} protocol buffer.
225 * @return a facet request protocol buffer with the values from this request
227 SearchServicePb
.FacetRequest
copyToProtocolBuffer() {
228 if (constraints
.isEmpty() && ranges
.isEmpty() && valueLimit
== null) {
229 return SearchServicePb
.FacetRequest
.newBuilder()
233 SearchServicePb
.FacetRequestParam
.Builder param
=
234 SearchServicePb
.FacetRequestParam
.newBuilder();
235 for (String constraint
: constraints
) {
236 param
.addValueConstraint(constraint
);
238 for (FacetRange range
: ranges
) {
239 SearchServicePb
.FacetRange
.Builder rangePb
= param
.addRangeBuilder();
240 if (range
.getStart() != null) {
241 rangePb
.setStart(range
.getStart());
243 if (range
.getEnd() != null) {
244 rangePb
.setEnd(range
.getEnd());
247 if (valueLimit
!= null) {
248 param
.setValueLimit(valueLimit
);
250 return SearchServicePb
.FacetRequest
.newBuilder()
252 .setParams(param
.build())
257 public String
toString() {
258 return new Util
.ToStringHelper("FacetRequest")
259 .addField("name", name
)
260 .addIterableField("valueConstraints", constraints
)
261 .addIterableField("ranges", ranges
)