Search in sources :

Example 11 with QueryPredicate

use of com.amplifyframework.core.model.query.predicate.QueryPredicate in project amplify-android by aws-amplify.

the class SQLiteCommandFactory method updateFor.

@NonNull
@Override
public <T extends Model> SqlCommand updateFor(@NonNull ModelSchema modelSchema, @NonNull T model) throws DataStoreException {
    final SQLiteTable table = SQLiteTable.fromSchema(modelSchema);
    final StringBuilder stringBuilder = new StringBuilder();
    stringBuilder.append("UPDATE").append(SqlKeyword.DELIMITER).append(Wrap.inBackticks(table.getName())).append(SqlKeyword.DELIMITER).append("SET").append(SqlKeyword.DELIMITER);
    // Previously, we figured out the correct column names from the model schema.
    // Instead of figuring out the correct column names again, just iterate
    // over whatever is actually there (since it was "right".)
    final List<SQLiteColumn> columns = table.getSortedColumns();
    final Iterator<SQLiteColumn> columnsIterator = columns.iterator();
    while (columnsIterator.hasNext()) {
        final String columnName = columnsIterator.next().getName();
        stringBuilder.append(Wrap.inBackticks(columnName)).append(SqlKeyword.DELIMITER).append(SqlKeyword.EQUAL).append(SqlKeyword.DELIMITER).append("?");
        if (columnsIterator.hasNext()) {
            stringBuilder.append(", ");
        }
    }
    // Append WHERE statement
    final SQLiteTable sqliteTable = SQLiteTable.fromSchema(modelSchema);
    final String primaryKeyName = sqliteTable.getPrimaryKeyColumnName();
    final QueryPredicate matchId = QueryField.field(primaryKeyName).eq(model.getId());
    SQLPredicate sqlPredicate = new SQLPredicate(matchId);
    stringBuilder.append(SqlKeyword.DELIMITER).append(SqlKeyword.WHERE).append(SqlKeyword.DELIMITER).append(sqlPredicate).append(";");
    final String preparedUpdateStatement = stringBuilder.toString();
    // SET clause
    List<Object> bindings = extractFieldValues(model);
    // WHERE clause
    bindings.addAll(sqlPredicate.getBindings());
    return new SqlCommand(table.getName(), preparedUpdateStatement, bindings);
}
Also used : SQLPredicate(com.amplifyframework.datastore.storage.sqlite.adapter.SQLPredicate) QueryPredicate(com.amplifyframework.core.model.query.predicate.QueryPredicate) SQLiteColumn(com.amplifyframework.datastore.storage.sqlite.adapter.SQLiteColumn) SQLiteTable(com.amplifyframework.datastore.storage.sqlite.adapter.SQLiteTable) NonNull(androidx.annotation.NonNull)

Example 12 with QueryPredicate

use of com.amplifyframework.core.model.query.predicate.QueryPredicate in project amplify-android by aws-amplify.

the class SQLiteCommandFactory method queryFor.

@NonNull
@Override
public SqlCommand queryFor(@NonNull ModelSchema modelSchema, @NonNull QueryOptions options) throws DataStoreException {
    final SQLiteTable table = SQLiteTable.fromSchema(modelSchema);
    final String tableName = table.getName();
    StringBuilder rawQuery = new StringBuilder();
    StringBuilder selectColumns = new StringBuilder();
    StringBuilder joinStatement = new StringBuilder();
    final List<Object> bindings = new ArrayList<>();
    // Track the list of columns to return, along with the alias for the corresponding table
    Map<String, List<SQLiteColumn>> columns = new HashMap<>();
    columns.put(table.getName(), table.getSortedColumns());
    Map<String, Integer> tableCount = new HashMap<>();
    tableCount.put(tableName, 1);
    // Joins the foreign keys
    recursivelyBuildJoins(table, columns, joinStatement, tableCount, tableName);
    // Convert columns to comma-separated column names
    boolean firstTable = true;
    for (String tableAlias : columns.keySet()) {
        if (!firstTable) {
            selectColumns.append(",").append(SqlKeyword.DELIMITER);
        } else {
            firstTable = false;
        }
        Iterator<SQLiteColumn> columnsIterator = Objects.requireNonNull(columns.get(tableAlias)).iterator();
        while (columnsIterator.hasNext()) {
            final SQLiteColumn column = columnsIterator.next();
            String columnName = column.getQuotedColumnName().replace(column.getTableName(), tableAlias);
            selectColumns.append(columnName);
            // Alias columns with a unique alias to avoid duplicate column names or alias names
            String columnAlias = column.getAliasedName() + tableAlias.substring(column.getTableName().length());
            selectColumns.append(SqlKeyword.DELIMITER).append(SqlKeyword.AS).append(SqlKeyword.DELIMITER).append(Wrap.inBackticks(columnAlias));
            if (columnsIterator.hasNext()) {
                selectColumns.append(",").append(SqlKeyword.DELIMITER);
            }
        }
    }
    // Start SELECT statement.
    // SELECT columns FROM tableName
    rawQuery.append(SqlKeyword.SELECT).append(SqlKeyword.DELIMITER).append(selectColumns.toString()).append(SqlKeyword.DELIMITER).append(SqlKeyword.FROM).append(SqlKeyword.DELIMITER).append(Wrap.inBackticks(tableName));
    // LEFT JOIN tableTwo ON tableName.id=tableTwo.foreignKey
    if (!joinStatement.toString().isEmpty()) {
        rawQuery.append(SqlKeyword.DELIMITER).append(joinStatement.toString());
    }
    // Append predicates.
    // WHERE condition
    final QueryPredicate predicate = options.getQueryPredicate();
    if (!QueryPredicates.all().equals(predicate)) {
        final SQLPredicate sqlPredicate = new SQLPredicate(predicate);
        bindings.addAll(sqlPredicate.getBindings());
        String sqlPredicateString = sqlPredicate.toString();
        if (predicate instanceof QueryPredicateOperation) {
            QueryPredicateOperation<?> predicateOperation = (QueryPredicateOperation<?>) predicate;
            String predicateOperationField = predicateOperation.field();
            if (predicateOperationField.equals(PrimaryKey.fieldName()) && predicateOperation.modelName() == null && predicateOperation.operator().type() == QueryOperator.Type.EQUAL) {
                // The WHERE condition is Where.id("some-ID") but no model name is given.
                sqlPredicateString = sqlPredicateString.replace(predicateOperationField, tableName + "." + predicateOperationField);
            }
        }
        rawQuery.append(SqlKeyword.DELIMITER).append(SqlKeyword.WHERE).append(SqlKeyword.DELIMITER).append(sqlPredicateString);
    }
    // Append order by
    final List<QuerySortBy> sortByList = options.getSortBy();
    if (sortByList != null) {
        rawQuery.append(SqlKeyword.DELIMITER).append(SqlKeyword.ORDER_BY).append(SqlKeyword.DELIMITER);
        Iterator<QuerySortBy> sortByIterator = sortByList.iterator();
        while (sortByIterator.hasNext()) {
            final QuerySortBy sortBy = sortByIterator.next();
            String modelName = Wrap.inBackticks(sortBy.getModelName());
            String fieldName = Wrap.inBackticks(sortBy.getField());
            if (modelName == null) {
                modelName = Wrap.inBackticks(tableName);
            }
            final String columnName = modelName + "." + fieldName;
            rawQuery.append(columnName).append(SqlKeyword.DELIMITER).append(SqlKeyword.fromQuerySortOrder(sortBy.getSortOrder()));
            if (sortByIterator.hasNext()) {
                rawQuery.append(",").append(SqlKeyword.DELIMITER);
            }
        }
    }
    // Append pagination after order by
    final QueryPaginationInput paginationInput = options.getPaginationInput();
    if (paginationInput != null) {
        rawQuery.append(SqlKeyword.DELIMITER).append(SqlKeyword.LIMIT).append(SqlKeyword.DELIMITER).append("?").append(SqlKeyword.DELIMITER).append(SqlKeyword.OFFSET).append(SqlKeyword.DELIMITER).append("?");
        bindings.add(paginationInput.getLimit());
        bindings.add(paginationInput.getPage() * paginationInput.getLimit());
    }
    rawQuery.append(";");
    final String queryString = rawQuery.toString();
    return new SqlCommand(table.getName(), queryString, bindings);
}
Also used : QueryPredicateOperation(com.amplifyframework.core.model.query.predicate.QueryPredicateOperation) QueryPredicate(com.amplifyframework.core.model.query.predicate.QueryPredicate) QuerySortBy(com.amplifyframework.core.model.query.QuerySortBy) HashMap(java.util.HashMap) ArrayList(java.util.ArrayList) SQLiteTable(com.amplifyframework.datastore.storage.sqlite.adapter.SQLiteTable) SQLPredicate(com.amplifyframework.datastore.storage.sqlite.adapter.SQLPredicate) SQLiteColumn(com.amplifyframework.datastore.storage.sqlite.adapter.SQLiteColumn) QueryPaginationInput(com.amplifyframework.core.model.query.QueryPaginationInput) ArrayList(java.util.ArrayList) List(java.util.List) NonNull(androidx.annotation.NonNull)

Example 13 with QueryPredicate

use of com.amplifyframework.core.model.query.predicate.QueryPredicate in project amplify-android by aws-amplify.

the class SyncProcessor method syncModel.

/**
 * Sync models for a given model class.
 * This involves three steps:
 *  1. Lookup the last time the model class was synced;
 *  2. Make a request to the AppSync endpoint. If the last sync time is within a recent window
 *     of time, then request a *delta* sync. If the last sync time is outside a recent window of time,
 *     perform a *base* sync. A base sync is preformed by passing null.
 *  3. Continue fetching paged results until !hasNextResult() or we have synced the max records.
 *
 * @param schema The schema of the model to sync
 * @param syncTime The time of a last successful sync.
 * @param <T> The type of model to sync.
 * @return a stream of all ModelWithMetadata&lt;T&gt; objects from all pages for the provided model.
 * @throws DataStoreException if dataStoreConfigurationProvider.getConfiguration() fails
 */
private <T extends Model> Flowable<List<ModelWithMetadata<T>>> syncModel(ModelSchema schema, SyncTime syncTime) throws DataStoreException {
    final Long lastSyncTimeAsLong = syncTime.exists() ? syncTime.toLong() : null;
    final Integer syncPageSize = dataStoreConfigurationProvider.getConfiguration().getSyncPageSize();
    final Integer syncMaxRecords = dataStoreConfigurationProvider.getConfiguration().getSyncMaxRecords();
    AtomicReference<Integer> recordsFetched = new AtomicReference<>(0);
    QueryPredicate predicate = queryPredicateProvider.getPredicate(schema.getName());
    // Create a BehaviorProcessor, and set the default value to a GraphQLRequest that fetches the first page.
    BehaviorProcessor<GraphQLRequest<PaginatedResult<ModelWithMetadata<T>>>> processor = BehaviorProcessor.createDefault(appSync.buildSyncRequest(schema, lastSyncTimeAsLong, syncPageSize, predicate));
    return processor.concatMap(request -> {
        if (isSyncRetryEnabled) {
            return syncPageWithRetry(request).toFlowable();
        } else {
            return syncPage(request).toFlowable();
        }
    }).doOnNext(paginatedResult -> {
        if (paginatedResult.hasNextResult()) {
            processor.onNext(paginatedResult.getRequestForNextResult());
        } else {
            processor.onComplete();
        }
    }).map(paginatedResult -> Flowable.fromIterable(paginatedResult).map(modelWithMetadata -> hydrateSchemaIfNeeded(modelWithMetadata, schema)).toList().blockingGet()).takeUntil(items -> recordsFetched.accumulateAndGet(items.size(), Integer::sum) >= syncMaxRecords);
}
Also used : DataStoreConfigurationProvider(com.amplifyframework.datastore.DataStoreConfigurationProvider) Single(io.reactivex.rxjava3.core.Single) DataStoreErrorHandler(com.amplifyframework.datastore.DataStoreErrorHandler) BehaviorProcessor(io.reactivex.rxjava3.processors.BehaviorProcessor) NonNull(androidx.annotation.NonNull) ModelWithMetadata(com.amplifyframework.datastore.appsync.ModelWithMetadata) GraphQLRequest(com.amplifyframework.api.graphql.GraphQLRequest) ModelProvider(com.amplifyframework.core.model.ModelProvider) DataStoreChannelEventName(com.amplifyframework.datastore.DataStoreChannelEventName) AppSync(com.amplifyframework.datastore.appsync.AppSync) SyncQueriesStartedEvent(com.amplifyframework.datastore.events.SyncQueriesStartedEvent) AtomicReference(java.util.concurrent.atomic.AtomicReference) ApiException(com.amplifyframework.api.ApiException) ArrayList(java.util.ArrayList) SchemaRegistry(com.amplifyframework.core.model.SchemaRegistry) Time(com.amplifyframework.util.Time) Schedulers(io.reactivex.rxjava3.schedulers.Schedulers) Consumer(com.amplifyframework.core.Consumer) ModelSchema(com.amplifyframework.core.model.ModelSchema) PaginatedResult(com.amplifyframework.api.graphql.PaginatedResult) QueryPredicate(com.amplifyframework.core.model.query.predicate.QueryPredicate) HubEvent(com.amplifyframework.hub.HubEvent) Amplify(com.amplifyframework.core.Amplify) Flowable(io.reactivex.rxjava3.core.Flowable) HubChannel(com.amplifyframework.hub.HubChannel) SerializedModel(com.amplifyframework.core.model.SerializedModel) Model(com.amplifyframework.core.model.Model) Completable(io.reactivex.rxjava3.core.Completable) Logger(com.amplifyframework.logging.Logger) Objects(java.util.Objects) DataStoreException(com.amplifyframework.datastore.DataStoreException) List(java.util.List) Cancelable(com.amplifyframework.core.async.Cancelable) AmplifyDisposables(com.amplifyframework.datastore.AmplifyDisposables) ForEach(com.amplifyframework.util.ForEach) Collections(java.util.Collections) GraphQLRequest(com.amplifyframework.api.graphql.GraphQLRequest) ModelWithMetadata(com.amplifyframework.datastore.appsync.ModelWithMetadata) QueryPredicate(com.amplifyframework.core.model.query.predicate.QueryPredicate) AtomicReference(java.util.concurrent.atomic.AtomicReference)

Example 14 with QueryPredicate

use of com.amplifyframework.core.model.query.predicate.QueryPredicate in project amplify-android by aws-amplify.

the class AppSyncRequestFactory method parsePredicate.

static Map<String, Object> parsePredicate(QueryPredicate queryPredicate) throws DataStoreException {
    if (QueryPredicates.all().equals(queryPredicate)) {
        return Collections.singletonMap("id", Collections.singletonMap("ne", null));
    }
    if (QueryPredicates.none().equals(queryPredicate)) {
        // id cannot be null, so match none
        return Collections.singletonMap("id", Collections.singletonMap("eq", null));
    }
    if (queryPredicate instanceof QueryPredicateOperation) {
        QueryPredicateOperation<?> qpo = (QueryPredicateOperation<?>) queryPredicate;
        QueryOperator<?> op = qpo.operator();
        return Collections.singletonMap(qpo.field(), Collections.singletonMap(appSyncOpType(op.type()), appSyncOpValue(op)));
    } else if (queryPredicate instanceof QueryPredicateGroup) {
        QueryPredicateGroup qpg = (QueryPredicateGroup) queryPredicate;
        if (QueryPredicateGroup.Type.NOT.equals(qpg.type())) {
            try {
                return Collections.singletonMap("not", parsePredicate(qpg.predicates().get(0)));
            } catch (IndexOutOfBoundsException exception) {
                throw new DataStoreException("Predicate group of type NOT must include a value to negate.", exception, "Check if you created a NOT condition in your Predicate with no included value.");
            }
        } else {
            List<Map<String, Object>> predicates = new ArrayList<>();
            for (QueryPredicate predicate : qpg.predicates()) {
                predicates.add(parsePredicate(predicate));
            }
            return Collections.singletonMap(qpg.type().toString().toLowerCase(Locale.getDefault()), predicates);
        }
    } else {
        throw new DataStoreException("Tried to parse an unsupported QueryPredicate", "Try changing to one of the supported values: QueryPredicateOperation, QueryPredicateGroup.");
    }
}
Also used : QueryPredicateOperation(com.amplifyframework.core.model.query.predicate.QueryPredicateOperation) DataStoreException(com.amplifyframework.datastore.DataStoreException) QueryPredicate(com.amplifyframework.core.model.query.predicate.QueryPredicate) QueryPredicateGroup(com.amplifyframework.core.model.query.predicate.QueryPredicateGroup) ArrayList(java.util.ArrayList) List(java.util.List)

Example 15 with QueryPredicate

use of com.amplifyframework.core.model.query.predicate.QueryPredicate in project amplify-android by aws-amplify.

the class AppSyncRequestFactory method buildSyncRequest.

/**
 * Builds the query document for base and delta sync.
 * If you provide lastSyncTime, it builds a delta sync, where the delta is computed
 * against the provided time. Otherwise, if you provide a null lastSyncTime, a
 * request doc is generated for a base sync.
 * @param modelSchema Schema Class for which we want to sync.
 * @param lastSync The last time synced. If not provided, do a base query.
 *                 If provided, do a delta query.
 * @param <T> The type of objects we are syncing
 * @return A string which contains a GraphQL query doc for an base/delta sync
 * @throws DataStoreException On Failure to inspect
 */
@NonNull
static <T> AppSyncGraphQLRequest<T> buildSyncRequest(@NonNull final ModelSchema modelSchema, @Nullable final Long lastSync, @Nullable final Integer limit, @NonNull final QueryPredicate predicate, @NonNull final AuthModeStrategyType strategyType) throws DataStoreException {
    try {
        AppSyncGraphQLRequest.Builder builder = AppSyncGraphQLRequest.builder().modelClass(modelSchema.getModelClass()).modelSchema(modelSchema).operation(QueryType.SYNC).requestAuthorizationStrategyType(strategyType).requestOptions(new DataStoreGraphQLRequestOptions()).responseType(TypeMaker.getParameterizedType(PaginatedResult.class, ModelWithMetadata.class, modelSchema.getModelClass()));
        if (lastSync != null) {
            builder.variable("lastSync", "AWSTimestamp", lastSync);
        }
        if (limit != null) {
            builder.variable("limit", "Int", limit);
        }
        if (!QueryPredicates.all().equals(predicate)) {
            String filterType = "Model" + Casing.capitalizeFirst(modelSchema.getName()) + "FilterInput";
            QueryPredicate syncPredicate = predicate;
            if (!(syncPredicate instanceof QueryPredicateGroup)) {
                // When a filter is provided, wrap it with a predicate group of type AND.  By doing this, it enables
                // AppSync to optimize the request by performing a DynamoDB query instead of a scan.  If the
                // provided syncPredicate is already a QueryPredicateGroup, this is not needed.  If the provided
                // group is of type AND, the optimization will occur.  If the top level group is OR or NOT, the
                // optimization is not possible anyway.
                syncPredicate = QueryPredicateGroup.andOf(syncPredicate);
            }
            builder.variable("filter", filterType, parsePredicate(syncPredicate));
        }
        return builder.build();
    } catch (AmplifyException amplifyException) {
        throw new DataStoreException("Failed to get fields for model.", amplifyException, "Validate your model file.");
    }
}
Also used : DataStoreException(com.amplifyframework.datastore.DataStoreException) AmplifyException(com.amplifyframework.AmplifyException) QueryPredicate(com.amplifyframework.core.model.query.predicate.QueryPredicate) AppSyncGraphQLRequest(com.amplifyframework.api.aws.AppSyncGraphQLRequest) PaginatedResult(com.amplifyframework.api.graphql.PaginatedResult) QueryPredicateGroup(com.amplifyframework.core.model.query.predicate.QueryPredicateGroup) NonNull(androidx.annotation.NonNull)

Aggregations

QueryPredicate (com.amplifyframework.core.model.query.predicate.QueryPredicate)31 Test (org.junit.Test)17 ArrayList (java.util.ArrayList)12 BlogOwner (com.amplifyframework.testmodels.commentsblog.BlogOwner)11 ModelSchema (com.amplifyframework.core.model.ModelSchema)9 DataStoreException (com.amplifyframework.datastore.DataStoreException)9 HashSet (java.util.HashSet)8 List (java.util.List)7 NonNull (androidx.annotation.NonNull)6 SQLPredicate (com.amplifyframework.datastore.storage.sqlite.adapter.SQLPredicate)6 Consumer (com.amplifyframework.core.Consumer)5 Cancelable (com.amplifyframework.core.async.Cancelable)5 Model (com.amplifyframework.core.model.Model)5 SerializedModel (com.amplifyframework.core.model.SerializedModel)5 SQLiteTable (com.amplifyframework.datastore.storage.sqlite.adapter.SQLiteTable)5 Action (com.amplifyframework.core.Action)4 TimeUnit (java.util.concurrent.TimeUnit)4 AmplifyException (com.amplifyframework.AmplifyException)3 Amplify (com.amplifyframework.core.Amplify)3 ModelProvider (com.amplifyframework.core.model.ModelProvider)3