package com.googlecode.objectify.impl; import com.google.appengine.api.datastore.AsyncDatastoreService; import com.google.appengine.api.datastore.FetchOptions; import com.google.appengine.api.datastore.PreparedQuery; import com.google.appengine.api.datastore.QueryResultIterable; import com.google.appengine.api.datastore.QueryResultIterator; import com.google.appengine.api.datastore.Transaction; import com.googlecode.objectify.Key; import com.googlecode.objectify.util.DatastoreUtils; import java.util.logging.Logger; /** * Logic for dealing with queries. * * @author Jeff Schnitzer <jeff@infohazard.org> */ public class QueryEngine { /** */ static final Logger log = Logger.getLogger(QueryEngine.class.getName()); /** */ protected LoaderImpl<?> loader; protected AsyncDatastoreService ads; protected Transaction transactionRaw; /** */ public QueryEngine(LoaderImpl<?> loader, AsyncDatastoreService ads, Transaction transactionRaw) { this.loader = loader; this.ads = ads; this.transactionRaw = transactionRaw; } /** * Perform a keys-only query. */ public <T> QueryResultIterable<Key<T>> queryKeysOnly(com.google.appengine.api.datastore.Query query, final FetchOptions fetchOpts) { assert query.isKeysOnly(); log.finest("Starting keys-only query"); final PreparedQuery pq = prepare(query); return new QueryResultIterable<Key<T>>() { @Override public QueryResultIterator<Key<T>> iterator() { return new KeysOnlyIterator<>(pq, fetchOpts); } }; } /** * Perform a keys-only plus batch gets. */ public <T> QueryResultIterable<T> queryHybrid(com.google.appengine.api.datastore.Query query, final FetchOptions fetchOpts) { assert !query.isKeysOnly(); log.finest("Starting hybrid query"); query = DatastoreUtils.cloneQuery(query).setKeysOnly(); final PreparedQuery pq = prepare(query); return new QueryResultIterable<T>() { @Override public QueryResultIterator<T> iterator() { return new ChunkingIterator<>(loader.createLoadEngine(), pq, new KeysOnlyIterator<T>(pq, fetchOpts), fetchOpts.getChunkSize()); } }; } /** * A normal, non-hybrid query */ public <T> QueryResultIterable<T> queryNormal(com.google.appengine.api.datastore.Query query, final FetchOptions fetchOpts) { assert !query.isKeysOnly(); log.finest("Starting normal query"); final PreparedQuery pq = prepare(query); final LoadEngine loadEngine = loader.createLoadEngine(); return new QueryResultIterable<T>() { @Override public QueryResultIterator<T> iterator() { return new ChunkingIterator<>(loadEngine, pq, new StuffingIterator<T>(pq, fetchOpts, loadEngine), fetchOpts.getChunkSize()); } }; } /** * A projection query. Bypasses the session entirely. */ public <T> QueryResultIterable<T> queryProjection(com.google.appengine.api.datastore.Query query, final FetchOptions fetchOpts) { assert !query.isKeysOnly(); assert !query.getProjections().isEmpty(); log.finest("Starting projection query"); final PreparedQuery pq = prepare(query); final LoadEngine loadEngine = loader.createLoadEngine(); return new QueryResultIterable<T>() { @Override public QueryResultIterator<T> iterator() { return new ProjectionIterator<>(pq.asQueryResultIterator(fetchOpts), loadEngine); } }; } /** * The fundamental query count operation. This is sufficiently different from normal query(). */ public int queryCount(com.google.appengine.api.datastore.Query query, FetchOptions fetchOpts) { PreparedQuery pq = prepare(query); return pq.countEntities(fetchOpts); } /** */ private PreparedQuery prepare(com.google.appengine.api.datastore.Query query) { return ads.prepare(transactionRaw, query); } }