Revision created by MOE tool push_codebase.
[gae.git] / java / src / main / com / google / appengine / api / search / Cursor.java
blob60588a0e3fec9f7542208af5c32202eb49c7d5f0
1 // Copyright 2012 Google Inc. All Rights Reserved.
3 package com.google.appengine.api.search;
5 import com.google.appengine.api.search.SearchServicePb.SearchParams;
6 import com.google.appengine.api.search.checkers.CursorChecker;
7 import com.google.appengine.api.search.checkers.Preconditions;
9 import java.io.Serializable;
11 /**
12 * Represents a cursor on the set of results found for executing a {@link Query}
13 * during a search on the {@link Index}.
14 * <p>
15 * For example, the following code shows how to use a cursor to get the
16 * next page of results
17 * <p>
18 * <pre>
19 * Index index = ...
20 * Cursor cursor = Cursor.newBuilder().build();
21 * Query query = Query.newBuilder().setOptions(
22 * QueryOptions.newBuilder().setCursor(cursor).build("some query"));
24 * // Get the first page of results
25 * Results&lt;ScoredDocument&gt; results = index.search(query);
27 * // process results
28 * ...
30 * // Get the next set of results from the returned cursor
31 * query = Query.newBuilder().setOptions(
32 * QueryOptions.newBuilder().setCursor(
33 * results.getCursor()).build("some query"));
35 * results = index.search(query);
36 * </pre>
37 * <p>
38 * Alternatively, you can get a cursor to continue from each of the returned
39 * results.
40 * <pre>
41 * Cursor cursor =
42 * Cursor.newBuilder().setPerResult(true).build();
43 * Query query = Query.newBuilder().setOptions(
44 * QueryOptions.newBuilder().setCursor(cursor).build("some query"));
46 * // Get the first page of results
47 * Results&lt;ScoredDocument&gt; results = index.search(query);
49 * // process results
50 * for (ScoredDocument result : results) {
51 * // choose a cursor from one of the results
52 * cursor = result.getCursor();
53 * }
55 * // Get the next set of results from the result's cursor
56 * query = Query.newBuilder().setOptions(
57 * QueryOptions.newBuilder().setCursor(cursor).build("some query"));
59 * results = index.search(query);
60 * </pre>
62 public final class Cursor implements Serializable {
63 private static final long serialVersionUID = 5983232584697220044L;
65 /**
66 * A builder which constructs Cursor objects.
68 public static final class Builder { private String webSafeString; private boolean perResult;
70 private Builder() {
73 /**
74 * Constructs a {@link Cursor} builder with the given request.
76 * @param request the search request to populate the builder
78 private Builder(Cursor request) {
79 setFromWebSafeString(request.toWebSafeString());
82 /**
83 * Sets whether or not to return a Cursor on each individual result
84 * in {@link Results} or a single cursor with all
85 * Results.
87 * @param perResult if True, then return a Cursor with each
88 * {@link ScoredDocument} result, otherwise return a single Cursor
89 * with {@link Results}
90 * @return this Builder
92 public Builder setPerResult(boolean perResult) {
93 this.perResult = perResult;
94 return this;
97 /**
98 * Construct the final message.
100 * @param webSafeString use a cursor returned from a
101 * previous set of search results as a starting point to retrieve
102 * the next set of results. This can get you better performance, and
103 * also improves the consistency of pagination through index updates
104 * @return the Cursor built from the parameters entered on this
105 * Builder
106 * @throws IllegalArgumentException if the cursor string is invalid
108 public Cursor build(String webSafeString) {
109 setFromWebSafeString(webSafeString);
110 return new Cursor(this);
114 * @param webSafeString a string from some other
115 * {@link Cursor#toWebSafeString}
117 private void setFromWebSafeString(String webSafeString) {
118 CursorChecker.checkCursor(webSafeString);
119 int colon = webSafeString.indexOf(":");
120 Preconditions.checkArgument(colon > 0 && colon < webSafeString.length(),
121 "Invalid format for cursor string");
122 String booleanString = webSafeString.substring(0, colon);
123 Preconditions.checkArgument("true".equals(booleanString) || "false".equals(booleanString),
124 "Invalid format of webSafeString");
125 this.perResult = Boolean.parseBoolean(booleanString);
126 this.webSafeString = webSafeString.substring(colon + 1);
130 * Construct the final message.
132 * @return the Cursor built from the parameters entered on this
133 * Builder
135 public Cursor build() {
136 return new Cursor(this);
140 private final String webSafeString; private final boolean perResult;
143 * Creates a search request from the builder.
145 * @param builder the search request builder to populate with
147 private Cursor(Builder builder) {
148 webSafeString = builder.webSafeString;
149 perResult = builder.perResult;
150 checkValid();
154 * A web safe string representing a cursor returned from a previous set of
155 * search results to use as a starting point to retrieve the next
156 * set of results. Can be null.
158 * @return a web safe string representation of the cursor
160 public String toWebSafeString() {
161 if (webSafeString == null) {
162 return null;
164 return perResult + ":" + webSafeString;
168 * @return whether the cursor is for all Results or one cursor per result
170 public boolean isPerResult() {
171 return perResult;
175 * Creates and returns a {@link Cursor} builder. Set the search request
176 * parameters and use the {@link Builder#build()} method to create a concrete
177 * instance of Cursor.
179 * @return a {@link Builder} which can construct a search request
181 public static Builder newBuilder() {
182 return new Builder();
186 * Creates a builder from the given request.
188 * @param request the search request for the builder to use
189 * to build another request
190 * @return this builder
192 public static Builder newBuilder(Cursor request) {
193 return new Builder(request);
197 * Checks the search specification is valid, specifically, has
198 * a non-null number of documents to return specification, a valid
199 * cursor if present, valid sort specification list, a valid
200 * collection of field names for sorting.
202 * @return this checked Cursor
203 * @throws IllegalArgumentException if some part of the specification is
204 * invalid
206 private Cursor checkValid() {
207 CursorChecker.checkCursor(webSafeString);
208 return this;
212 * Copies the contents of this {@link Cursor} object into a
213 * {@link SearchParams} protocol buffer builder.
215 * @return a search params protocol buffer builder initialized with
216 * the values from this request
217 * @throws IllegalArgumentException if the cursor type is
218 * unknown
220 SearchParams.Builder copyToProtocolBuffer(SearchParams.Builder builder) {
221 if (webSafeString != null) {
222 builder.setCursor(webSafeString);
224 if (perResult) {
225 builder.setCursorType(SearchParams.CursorType.PER_RESULT);
226 } else {
227 builder.setCursorType(SearchParams.CursorType.SINGLE);
229 return builder;
232 @Override
233 public String toString() {
234 return new Util.ToStringHelper("Cursor")
235 .addField("webSafeString", toWebSafeString())
236 .finish();