Revision created by MOE tool push_codebase.
[gae.git] / java / src / main / com / google / appengine / api / search / FacetRequest.java
blob842f919814cfa1c7f202ace48c2710d3d9cc8e8c
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;
12 /**
13 * A facet request representing parameters for requesting specific facets to be returned with a
14 * query result.
15 * <p>
16 * For example, to request a facet with a name and specific values:
17 * <pre>
18 * FacetRequest request = FacetRequest.newBuilder().setName("wine_type")
19 * .addValueConstraint("white").addValueConstraint("red").build();
20 * </pre>
21 * and to request ranges:
22 * <pre>
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
27 * </pre>
29 public final class FacetRequest {
31 /**
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 {
38 private String name;
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();
52 private Builder() {
55 /**
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);
64 return this;
67 /**
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);
76 return this;
79 /**
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);
96 return this;
99 /**
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);
114 ranges.add(range);
115 return this;
119 * Construct the final message.
121 * @return the FacetRequest built from the parameters entered on this
122 * Builder
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;
142 checkValid();
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() {
172 return name;
176 * Returns the maximum number of values this facet should have. Null if the value limit
177 * is not set.
180 public Integer getValueLimit() {
181 return valueLimit;
185 * Returns an unmodifiable list of {@link FacetRange}s.
188 public List<FacetRange> getRanges() {
189 return ranges;
193 * Returns an unmodifiable list of value constraints.
196 public List<String> getValueConstraints() {
197 return constraints;
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
206 * invalid
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()
230 .setName(name)
231 .build();
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()
251 .setName(name)
252 .setParams(param.build())
253 .build();
256 @Override
257 public String toString() {
258 return new Util.ToStringHelper("FacetRequest")
259 .addField("name", name)
260 .addIterableField("valueConstraints", constraints)
261 .addIterableField("ranges", ranges)
262 .finish();