/* * This software is released under the GNU Lesser General Public License v3. * For more information see http://www.gnu.org/licenses/lgpl.html * * Copyright (c) 2011, Peter Knego & Matjaz Tercelj * All rights reserved. */ package com.leanengine; import java.util.ArrayList; import java.util.List; /** * Class to perform data queries against server datastore. * <br/><br/> */ public class LeanQuery { private final String kind; private List<QueryFilter> filters = new ArrayList<QueryFilter>(); private List<QuerySort> sorts = new ArrayList<QuerySort>(); private String cursor; private int limit = 20; private int offset = 0; /** * Constructor to initialize query. * * @param kind The kind (name) of entities to search for. */ public LeanQuery(String kind) { this.kind = kind; } /** * Executes the query and fetches the initial data set. This method executes in the background thread. * <br/><br/> * The result set returned depends on the {@link #limit(int)} and {@link #offset(int)} called on this query. * The defaults are: {@code limit = 20} and {@code offset = 0} as defined by the Google AppEngine. * <br/><br/> * To get the next batch of results use {@link #fetchNextInBackground(NetworkCallback)} or {@link #fetchNext()}. * * @param callback {@link NetworkCallback} that will be invoked when background task is done. */ public void fetchInBackground(NetworkCallback<LeanEntity> callback) { // this is initial fetch, reset the cursor cursor = null; RestService.getInstance().queryPrivateAsync(this, callback); } /** * Executes the query and fetches the next data set. This method executes in the background thread. * <br/><br/> * This method may only be called after {@link #fetch()} or {@link #fetchInBackground(NetworkCallback)} are invoked. * <br/><br/> * Query parameters must not be changed between invocation of {@code fetch()} and {@code fetchNext()} methods. * * @param callback {@link NetworkCallback} that will be invoked when background task is done. * @throws IllegalStateException If called before {@link #fetch()} or {@link #fetchInBackground(NetworkCallback)}. */ public void fetchNextInBackground(NetworkCallback<LeanEntity> callback) throws IllegalStateException { if (cursor == null) throw new IllegalStateException("This method can be only called after method 'fetch()' or 'fetchInBackground(..)' is executed."); RestService.getInstance().queryPrivateAsync(this, callback); } /** * Executes the query and fetches the initial data set. * <br/><br/> * The results returned depend on the {@link #limit(int)} and {@link #offset(int)} called on this query. * The defaults are: {@code limit = 20} and {@code offset = 0} as defined by the Google AppEngine. * <br/><br/> * To get the next batch of results use {@link #fetchNext()} or {@link #fetchNextInBackground(NetworkCallback)}. * * @return The array of LeanEntities representing the result of query. * @throws LeanException In case of errors a {@link LeanException} is thrown. * @throws IllegalStateException If called before {@link #fetch()} or {@link #fetchInBackground(NetworkCallback)}. */ public LeanEntity[] fetch() throws LeanException { // this is initial fetch, reset the cursor cursor = null; return RestService.getInstance().queryPrivate(this); } /** * Executes the query and fetches the next data set. * <br/><br/> * This method may only be called after {@link #fetch()} or {@link #fetchInBackground(NetworkCallback)} are invoked. * <br/><br/> * Query parameters must not be changed between invocation of {@code fetch()} and {@code fetchNext()} methods. * * @return The array of LeanEntities representing the result of query. * @throws LeanException In case of errors a {@link LeanException} is thrown. * @throws IllegalStateException If called before {@link #fetch()} or {@link #fetchInBackground(NetworkCallback)}. */ public LeanEntity[] fetchNext() throws LeanException { if (cursor == null) throw new IllegalStateException("This method can be only called after method 'fetch()' or 'fetchInBackground(..)' is executed."); return RestService.getInstance().queryPrivate(this); } String getCursor() { return cursor; } LeanQuery setCursor(String cursor) { this.cursor = cursor; return this; } /** * Adds a filter on the specified property. * * @param property The name of the property to which the filter applies. * @param operator A {@link FilterOperator} to use. * @param value An instance of a supported datastore type. * @return {@link this} (for chaining) */ public LeanQuery addFilter(String property, FilterOperator operator, Object value) { filters.add(new QueryFilter(property, operator, value)); return this; } /** * Adds a sort on the specified property. * * @param property The name of the property to which the sort applies. * @param direction A {@link SortDirection} to use. * @return {@link this} (for chaining) */ public LeanQuery addSort(String property, SortDirection direction) { sorts.add(new QuerySort(property, direction)); return this; } /** * Returns the kind (name) of entities this query is fetching. * * @return Kind of entity. */ public String getKind() { return kind; } /** * Returns a list of QuerySorts set on this query. * * @return {@link List}<{@link QuerySort}> */ public List<QuerySort> getSorts() { return sorts; } /** * Returns a list of QueryFilters set on this query. * * @return {@link List}<{@link QueryFilter}> */ public List<QueryFilter> getFilters() { return filters; } /** * The size of result set returned by this query's fetch methods. * * @param limit The size of result set. * @return {@link this} (for chaining) */ public LeanQuery limit(int limit) { this.limit = limit; return this; } /** * Returns the limit set on this query - the size of result set to return when fetching. * See {@link #limit(int)} * * @return The size of result set. */ public int getLimit() { return limit; } /** * Returns the offset set on this query - the number of results to skip before returning any results. * See {@link #offset(int)} * * @return The number of results to skip before returning the result set. */ public int getOffset() { return offset; } /** * Sets the offset - the number of results to skip before returning any results. * <br/><br/> * <b>Note: this method has a high cost in terms of datastore access charges - all entities skipped by the offset * count as retrieved in AppEngine billing.</b> * * @param offset The number of results to skip before returning the result set. * @return {@link this} (for chaining) */ public LeanQuery offset(int offset) { this.offset = offset; return this; } /** * FilterOperator specifies what type of operation you want to apply to your filter. */ public enum FilterOperator { IN("IN"), EQUAL("="), GREATER_THAN(">"), GREATER_THAN_OR_EQUAL(">="), LESS_THAN("<"), LESS_THAN_OR_EQUAL("<="), NOT_EQUAL("!="); private String operatorString; static FilterOperator create(String jsonOperator) { if ("=".equals(jsonOperator)) { return FilterOperator.EQUAL; } else if (">".equals(jsonOperator)) { return FilterOperator.GREATER_THAN; } else if (">=".equals(jsonOperator)) { return FilterOperator.GREATER_THAN_OR_EQUAL; } else if ("<".equals(jsonOperator)) { return FilterOperator.LESS_THAN; } else if ("<=".equals(jsonOperator)) { return FilterOperator.LESS_THAN_OR_EQUAL; } else if ("!=".equals(jsonOperator)) { return FilterOperator.NOT_EQUAL; } else if ("IN".equals(jsonOperator)) { return FilterOperator.IN; } throw new IllegalArgumentException("Unsupported query operator: '" + jsonOperator + "'"); } FilterOperator(String operatorString) { this.operatorString = operatorString; } String toJSON() { return operatorString; } } /** * SortDirection controls the order of a sort. */ public enum SortDirection { ASCENDING("asc"), DESCENDING("desc"); private String sortString; SortDirection(String sortString) { this.sortString = sortString; } static SortDirection create(String sortJson) { if ("asc".equals(sortJson)) { return SortDirection.ASCENDING; } else if ("desc".equals(sortJson)) { return SortDirection.DESCENDING; } throw new IllegalArgumentException("Unsupported sort operator: '" + sortJson + "'"); } String toJSON() { return sortString; } } }