1 // Copyright 2010 Google Inc. All Rights Reserved.
3 package com
.google
.appengine
.api
.search
;
5 import com
.google
.appengine
.api
.search
.checkers
.FieldChecker
;
6 import com
.google
.appengine
.api
.search
.checkers
.Preconditions
;
11 * Sorting specification for a single dimension. Multi-dimensional sorting
12 * is supported by a collection of SortExpressions.
15 public final class SortExpression
{
18 * The expression to be used if you wish to sort by rank field.
19 * By default, results are sorted in descending value of rank.
20 * To sort in ascending order, you need to create a sort expression as
22 * SortExpression expr = SortExpression.newBuilder()
23 * .setExpression(SortExpression.RANK_FIELD_NAME)
24 * .setDirection(SortExpression.SortDirection.ASCENDING)
25 * .setDefaultValueNumeric(0)
29 public static final String RANK_FIELD_NAME
= "_order_id";
32 * The expression to be used if you wish to sort by document score.
33 * You need to create a sort expression as
35 * SortExpression expr = SortExpression.newBuilder()
36 * .setExpression(String.format(
37 * "%s + rating * 0.01", SortExpression.SCORE_FIELD_NAME))
38 * .setDirection(SortExpression.SortDirection.DESCENDING)
39 * .setDefaultValueNumeric(0)
43 public static final String SCORE_FIELD_NAME
= "_score";
46 * The direction search results are sorted by, either ascending or descending.
48 public enum SortDirection
{
50 * The search results are sorted in ascending order,
51 * e.g. alphabetic aaa ... zzz
56 * The search results are sorted in descending order,
63 * A builder that constructs {@link SortExpression SortExpressions}. The user
64 * must provide an expression. The expression can be as simple as a field
65 * name, or can be some other expression such as <code>"score +
66 * count(likes) * 0.1"</code>, which combines a scorer score with a count
67 * of the number of likes values times 0.1. A default value must be specified
71 * SortExpression expr = SortExpression.newBuilder()
72 * .setExpression(String.format(
73 * "%s + count(likes) * 0.1", SortExpression.SCORE_FIELD_NAME))
74 * .setDirection(SortExpression.SortDirection.ASCENDING)
75 * .setDefaultValueNumeric(0.0)
79 public static final class Builder
{
80 private String expression
;
81 private SortDirection direction
; private String defaultValue
; private Double defaultValueNumeric
; private Date defaultValueDate
;
87 * Sets an expression to be evaluated for each document to sort by.
88 * A default string value {@link #setDefaultValue(String)} or numeric
89 * {@link #setDefaultValueNumeric(double)} or date
90 * {@link #setDefaultValueDate(Date)} must be specified for the expression.
92 * @param expression the expression to evaluate for each
94 * @return this Builder
95 * @throws IllegalArgumentException if the expression is invalid
97 public Builder
setExpression(String expression
) {
98 this.expression
= FieldChecker
.checkExpression(expression
);
103 * Sets the direction to sort the search results in.
105 * @param direction the direction to sort the search results in. The
106 * default direction is {@link SortDirection#DESCENDING}
107 * @return this Builder
109 public Builder
setDirection(SortDirection direction
) {
110 this.direction
= direction
;
115 * Sets the default value for the field for sorting purposes. Must provide
118 * @param defaultValue the default value for the field
119 * @return this Builder
120 * @throws IllegalArgumentException if the {@code defaultValue} is not valid
122 public Builder
setDefaultValue(String defaultValue
) {
123 this.defaultValue
= FieldChecker
.checkText(defaultValue
);
128 * Sets the default value for the field for sorting purposes. Must provide
131 * @param defaultValue the default value for the field
132 * @return this Builder
134 public Builder
setDefaultValueNumeric(double defaultValue
) {
135 this.defaultValueNumeric
= defaultValue
;
140 * Sets the default value for the field for sorting purposes. Must provide
141 * for Date sorts. Typically, you should use {@link DateUtil#MIN_DATE} or
142 * {@link DateUtil#MAX_DATE} as a default value.
144 * @param defaultValue the default value for the field
145 * @return this Builder
147 public Builder
setDefaultValueDate(Date defaultValue
) {
148 this.defaultValueDate
= FieldChecker
.checkDate(defaultValue
);
153 * Builds a {@link SortExpression} from the set values.
155 * @return a {@link SortExpression} built from the set values
156 * @throws IllegalArgumentException if the field name or
157 * default value is invalid
159 public SortExpression
build() {
160 return new SortExpression(this);
164 private final SortDirection direction
;
165 private final String expression
;
167 private final String defaultValue
;
168 private final Double defaultValueNumeric
;
169 private final Date defaultValueDate
;
172 * Constructs a text sort specification using the values from the
175 private SortExpression(Builder builder
) {
176 this.expression
= builder
.expression
;
177 this.direction
= Util
.defaultIfNull(builder
.direction
, SortDirection
.DESCENDING
);
178 this.defaultValue
= builder
.defaultValue
;
179 this.defaultValueNumeric
= builder
.defaultValueNumeric
;
180 this.defaultValueDate
= builder
.defaultValueDate
;
185 * @return the expression to evaluate for each document and sort by
187 public String
getExpression() {
192 * @return the direction to sort the search results in
194 public SortDirection
getDirection() {
199 * @return the default value for the field. Can be null
201 public String
getDefaultValue() {
206 * @return the default numeric value for the field. Can be null
208 public Double
getDefaultValueNumeric() {
209 return defaultValueNumeric
;
213 * @return the default date value for the field. Can be null
215 public Date
getDefaultValueDate() {
216 return defaultValueDate
;
220 * Checks whether this sort specification is valid.
222 * @return this {@code SortExpression}
223 * @throws IllegalArgumentException if the expression is null or invalid,
224 * or no default value for the expression is specified or a default string
227 private SortExpression
checkValid() {
228 Preconditions
.checkNotNull(expression
, "expression cannot be null");
229 int defaultValueCount
= 0;
230 if (defaultValue
!= null) {
233 if (defaultValueNumeric
!= null) {
236 if (defaultValueDate
!= null) {
239 Preconditions
.checkArgument(defaultValueCount
== 1,
240 "Exactly one default value must be specified for the SortExpression");
241 if (defaultValue
!= null) {
242 FieldChecker
.checkText(defaultValue
);
248 * Copies this sort specification object contents into a protocol buffer.
250 * @return the protocol buffer with the contents of the {@code sortSpec}
252 SearchServicePb
.SortSpec
copyToProtocolBuffer() {
253 SearchServicePb
.SortSpec
.Builder builder
= SearchServicePb
.SortSpec
.newBuilder();
254 if (SortDirection
.ASCENDING
.equals(getDirection())) {
255 builder
.setSortDescending(false);
257 builder
.setSortExpression(getExpression());
258 if (getDefaultValue() != null) {
259 builder
.setDefaultValueText(getDefaultValue());
261 if (getDefaultValueNumeric() != null) {
262 builder
.setDefaultValueNumeric(getDefaultValueNumeric());
264 if (getDefaultValueDate() != null) {
265 builder
.setDefaultValueText(DateUtil
.serializeDate(getDefaultValueDate()));
267 return builder
.build();
271 * Creates and returns a SortExpression Builder.
273 * @return a new {@link SortExpression.Builder}. Set the parameters for the sort
274 * specification on the Builder, and use the {@link Builder#build()} method
275 * to create a concrete instance of SortExpression
277 public static Builder
newBuilder() {
278 return new Builder();
282 public String
toString() {
283 return String
.format("SortExpression(direction=%s%s%s%s)",
285 Util
.fieldToString("expression", expression
),
286 Util
.fieldToString("defaultValue", defaultValue
),
287 Util
.fieldToString("defaultValueNumeric", defaultValueNumeric
),
288 Util
.fieldToString("defaultValueDate", defaultValueDate
));