1717
1818import android .database .Cursor ;
1919import android .database .sqlite .SQLiteDatabase ;
20+ import android .database .sqlite .SQLiteException ;
2021import androidx .annotation .NonNull ;
2122
2223import com .amplifyframework .core .Amplify ;
2324import com .amplifyframework .core .model .Model ;
2425import com .amplifyframework .core .model .ModelAssociation ;
2526import com .amplifyframework .core .model .ModelSchema ;
2627import com .amplifyframework .core .model .ModelSchemaRegistry ;
27- import com .amplifyframework .core .model .query .QueryOptions ;
28- import com .amplifyframework .core .model .query .Where ;
29- import com .amplifyframework .core .model .query .predicate .QueryField ;
30- import com .amplifyframework .core .model .query .predicate .QueryPredicate ;
31- import com .amplifyframework .core .model .query .predicate .QueryPredicates ;
32- import com .amplifyframework .datastore .DataStoreException ;
3328import com .amplifyframework .datastore .appsync .SerializedModel ;
3429import com .amplifyframework .datastore .storage .sqlite .adapter .SQLiteTable ;
3530import com .amplifyframework .logging .Logger ;
3631import com .amplifyframework .util .Empty ;
3732import com .amplifyframework .util .GsonFactory ;
33+ import com .amplifyframework .util .Wrap ;
3834
3935import com .google .gson .Gson ;
4036
4137import java .util .ArrayList ;
4238import java .util .Collection ;
4339import java .util .Collections ;
4440import java .util .HashSet ;
41+ import java .util .Iterator ;
4542import java .util .LinkedHashMap ;
4643import java .util .List ;
4744import java .util .Map ;
@@ -54,21 +51,17 @@ final class SQLiteModelTree {
5451 private static final Logger LOG = Amplify .Logging .forNamespace ("amplify:aws-datastore" );
5552
5653 private final ModelSchemaRegistry registry ;
57- private final SQLCommandFactory commandFactory ;
5854 private final SQLiteDatabase database ;
5955 private final Gson gson ;
6056
6157 /**
6258 * Constructs a model family tree traversing utility.
6359 * @param registry model registry to search schema from
64- * @param commandFactory SQL command factory
6560 * @param database SQLite database connection handle
6661 */
6762 SQLiteModelTree (ModelSchemaRegistry registry ,
68- SQLCommandFactory commandFactory ,
6963 SQLiteDatabase database ) {
7064 this .registry = registry ;
71- this .commandFactory = commandFactory ;
7265 this .database = database ;
7366 this .gson = GsonFactory .instance ();
7467 }
@@ -119,38 +112,28 @@ private void recurseTree(
119112 ModelSchema modelSchema ,
120113 Collection <String > parentIds
121114 ) {
122- SQLiteTable parentTable = SQLiteTable .fromSchema (modelSchema );
123- final String parentTableName = parentTable .getName ();
124- final String parentPrimaryKeyName = parentTable .getPrimaryKey ().getName ();
125115 for (ModelAssociation association : modelSchema .getAssociations ().values ()) {
126116 switch (association .getName ()) {
127117 case "HasOne" :
128118 case "HasMany" :
129119 String childModel = association .getAssociatedType (); // model name
130120 ModelSchema childSchema = registry .getModelSchemaForModelClass (childModel );
131121 SQLiteTable childTable = SQLiteTable .fromSchema (childSchema );
132- String childPrimaryKey = childTable .getPrimaryKey ().getAliasedName ();
133- QueryField queryField = QueryField .field (parentTableName , parentPrimaryKeyName );
134-
135- // Chain predicates with OR operator.
136- QueryPredicate predicate = QueryPredicates .none ();
137- for (String parentId : parentIds ) {
138- QueryPredicate operation = queryField .eq (parentId );
139- predicate = predicate .or (operation );
140- }
122+ String childId = childTable .getPrimaryKey ().getName ();
123+ String parentId = childSchema .getAssociations () // get a map of associations
124+ .get (association .getAssociatedName ()) // get @BelongsTo association linked to this field
125+ .getTargetName (); // get the target field (parent) name
141126
142127 // Collect every children one level deeper than current level
143- // SELECT * FROM <CHILD_TABLE> WHERE <PARENT> = <ID_1> OR <PARENT> = <ID_2> OR ...
144- QueryOptions options = Where .matches (predicate );
145128 Set <String > childrenIds = new HashSet <>();
146- try (Cursor cursor = queryAll ( childModel , options )) {
129+ try (Cursor cursor = queryChildren ( childTable . getName (), childId , parentId , parentIds )) {
147130 if (cursor != null && cursor .moveToFirst ()) {
148- int index = cursor .getColumnIndexOrThrow (childPrimaryKey );
131+ int index = cursor .getColumnIndexOrThrow (childId );
149132 do {
150133 childrenIds .add (cursor .getString (index ));
151134 } while (cursor .moveToNext ());
152135 }
153- } catch (DataStoreException exception ) {
136+ } catch (SQLiteException exception ) {
154137 // Don't cut the search short. Populate rest of the tree.
155138 LOG .error ("Failed to query children of deleted model(s)." , exception );
156139 }
@@ -172,15 +155,38 @@ private void recurseTree(
172155 }
173156 }
174157
175- private Cursor queryAll (
176- @ NonNull String tableName ,
177- @ NonNull QueryOptions options
178- ) throws DataStoreException {
179- final ModelSchema schema = registry .getModelSchemaForModelClass (tableName );
180- final SqlCommand sqlCommand = commandFactory .queryFor (schema , options );
181- final String rawQuery = sqlCommand .sqlStatement ();
182- final String [] bindings = sqlCommand .getBindingsAsArray ();
183- return database .rawQuery (rawQuery , bindings );
158+ private Cursor queryChildren (
159+ @ NonNull String childTable ,
160+ @ NonNull String childIdField ,
161+ @ NonNull String parentIdField ,
162+ @ NonNull Collection <String > parentIds
163+ ) {
164+ // Wrap each ID with single quote
165+ StringBuilder quotedIds = new StringBuilder ();
166+ for (Iterator <String > ids = parentIds .iterator (); ids .hasNext ();) {
167+ quotedIds .append (Wrap .inSingleQuotes (ids .next ()));
168+ if (ids .hasNext ()) {
169+ quotedIds .append (SqlKeyword .SEPARATOR );
170+ }
171+ }
172+ // SELECT <child_id> FROM <child_table> WHERE <parent_id> IN (<id_1>, <id_2>, ...)
173+ String queryString = String .valueOf (SqlKeyword .SELECT ) +
174+ SqlKeyword .DELIMITER +
175+ Wrap .inBackticks (childIdField ) +
176+ SqlKeyword .DELIMITER +
177+ SqlKeyword .FROM +
178+ SqlKeyword .DELIMITER +
179+ Wrap .inBackticks (childTable ) +
180+ SqlKeyword .DELIMITER +
181+ SqlKeyword .WHERE +
182+ SqlKeyword .DELIMITER +
183+ Wrap .inBackticks (parentIdField ) +
184+ SqlKeyword .DELIMITER +
185+ SqlKeyword .IN +
186+ SqlKeyword .DELIMITER +
187+ Wrap .inParentheses (quotedIds .toString ()) +
188+ ";" ;
189+ return database .rawQuery (queryString , new String [0 ]);
184190 }
185191
186192 private String getModelName (@ NonNull Model model ) {
0 commit comments