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);
}
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);
}
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<T> 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);
}
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.");
}
}
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.");
}
}
Aggregations