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
;
12 * Represents a cursor on the set of results found for executing a {@link Query}
13 * during a search on the {@link Index}.
15 * For example, the following code shows how to use a cursor to get the
16 * next page of results
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<ScoredDocument> results = index.search(query);
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);
38 * Alternatively, you can get a cursor to continue from each of the returned
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<ScoredDocument> results = index.search(query);
50 * for (ScoredDocument result : results) {
51 * // choose a cursor from one of the results
52 * cursor = result.getCursor();
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);
62 public final class Cursor
implements Serializable
{
63 private static final long serialVersionUID
= 5983232584697220044L;
66 * A builder which constructs Cursor objects.
68 public static final class Builder
{ private String webSafeString
; private boolean perResult
;
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());
83 * Sets whether or not to return a Cursor on each individual result
84 * in {@link Results} or a single cursor with all
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
;
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
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
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
;
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) {
164 return perResult
+ ":" + webSafeString
;
168 * @return whether the cursor is for all Results or one cursor per result
170 public boolean isPerResult() {
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
206 private Cursor
checkValid() {
207 CursorChecker
.checkCursor(webSafeString
);
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
220 SearchParams
.Builder
copyToProtocolBuffer(SearchParams
.Builder builder
) {
221 if (webSafeString
!= null) {
222 builder
.setCursor(webSafeString
);
225 builder
.setCursorType(SearchParams
.CursorType
.PER_RESULT
);
227 builder
.setCursorType(SearchParams
.CursorType
.SINGLE
);
233 public String
toString() {
234 return new Util
.ToStringHelper("Cursor")
235 .addField("webSafeString", toWebSafeString())