All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.appengine.api.datastore.Cursor Maven / Gradle / Ivy

Go to download

API for Google App Engine standard environment with some of the dependencies shaded (repackaged)

There is a newer version: 2.0.27
Show newest version
/*
 * Copyright 2021 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.appengine.api.datastore;

import static com.google.appengine.api.datastore.FetchOptions.Builder.withStartCursor;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.io.BaseEncoding.base64Url;

import com.google.apphosting.datastore.DatastoreV3Pb.Query;
import com.google.common.base.CharMatcher;
import com.google.common.io.ByteStreams;
import com.google.protobuf.ByteString;
import java.io.IOException;
import java.io.Serializable;
import org.checkerframework.checker.nullness.qual.Nullable;

/**
 * A cursor that represents a position in a query.
 *
 * 

To resume a {@link Query} at the position defined by a {@link Cursor}, the {@link Cursor} must * be present in the {@link FetchOptions} passed to a {@link PreparedQuery} identical to the one it * was created from. * *

Cursors can be retrieved from {@code PreparedQuery.asQueryResult*} functions. A typical use * case would be: * *

* *
 * Cursor originalCursor = preparedQuery.asQueryResultList(withLimit(20)).getCursor();
 * String encodedCursor = original.toWebSafeString();
 * 
* *
* * The encoded cursor can then be passed safely in a get or post arg of a web request and on another * request the next batch of results can be retrieved with: * *
* *
{@code
 * Cursor decodedCursor = Cursor.fromWebSafeString(encodedCursor);
 * List nextBatch = preparedQuery.asQueryResultList(withLimit(20).cursor(decoded));
 * }
* *
* */ public final class Cursor implements Serializable { static final long serialVersionUID = 3515556366838971499L; // NOTE: Ideally this variable should be final but we need to // support custom serialization as we have no control over the // serialization of CompiledQuery and have already been broken by a change // to its base class. // Since we don't use default serialization, we can mark the field transient, to signal // to class scanners that it is not part of the serial form. private transient ByteString cursorBytes; // Constructor is package private Cursor() { cursorBytes = ByteString.EMPTY; } Cursor(Cursor previousCursor) { this(previousCursor.cursorBytes); } Cursor(ByteString cursorBytes) { checkNotNull(cursorBytes); this.cursorBytes = cursorBytes; } // Serialization functions private void writeObject(java.io.ObjectOutputStream out) throws IOException { out.write(cursorBytes.toByteArray()); } private void readObject(java.io.ObjectInputStream in) throws IOException { cursorBytes = ByteString.copyFrom(ByteStreams.toByteArray(in)); } // TODO: consider exposing to users Cursor advance(final int n, PreparedQuery query) { if (n == 0) { return this; } else if (n > 0) { return query.asQueryResultIterator(withStartCursor(this).offset(n).limit(0)).getCursor(); } throw new IllegalArgumentException("Unable to offset cursor by " + n + " results."); } /** * Returns a cursor identical to {@code this}. * * @deprecated It is no longer necessary to call {@link #reverse()} on cursors. *

A cursor returned by a query may also be used in the query returned by {@link * com.google.appengine.api.datastore.Query#reverse()}. */ @Deprecated public Cursor reverse() { return this; } /** * Encodes the current cursor as a web safe string that can later be decoded by {@link * #fromWebSafeString(String)} */ public String toWebSafeString() { return base64Url().omitPadding().encode(cursorBytes.toByteArray()); } /** * Decodes the given encoded cursor * * @return the decoded cursor * @throws IllegalArgumentException if the provided string is not a valid encoded cursor */ public static Cursor fromWebSafeString(String encodedCursor) { checkNotNull(encodedCursor, "encodedCursor must not be null"); try { return new Cursor( ByteString.copyFrom( base64Url().decode(CharMatcher.whitespace().removeFrom(encodedCursor)))); } catch (IllegalArgumentException e) { throw new IllegalArgumentException("Unable to decode provided cursor.", e); } } ByteString toByteString() { return ByteString.copyFrom(cursorBytes.toByteArray()); } @Override public boolean equals(@Nullable Object obj) { if (obj == null) { return false; } if (obj.getClass() != this.getClass()) { return false; } return cursorBytes.equals(((Cursor) obj).cursorBytes); } @Override public int hashCode() { return cursorBytes.hashCode(); } @Override public String toString() { return cursorBytes.toString(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy