## Selectively syncing a subset of your data By default, DataStore fetches all the records that you’re authorized to access from your cloud data source to your local device. The maximum number of records that will be stored locally is configurable [here](/lib/datastore/conflict). You can utilize selective sync to persist a subset of your data instead. Selective sync works by applying predicates to the base and delta sync queries, as well as to incoming subscriptions. Note that selective sync is applied on top of authorization rules you’ve defined on your schema with the `@auth` directive. For more information see the [Setup authorization rules](/lib/datastore/setup-auth-rules/) section. ```java Amplify.addPlugin(AWSDataStorePlugin.builder().dataStoreConfiguration( DataStoreConfiguration.builder() .syncExpression(Post.class, () -> Post.RATING.gt(5)) .syncExpression(Comment.class, () -> Comment.STATUS.eq("active")) .build()) .build()); ``` ```kotlin Amplify.addPlugin(AWSDataStorePlugin.builder().dataStoreConfiguration( DataStoreConfiguration.builder() .syncExpression(Post::class.java) { Post.RATING.gt(5) } .syncExpression(Comment::class.java) { Comment.STATUS.eq("active") } .build()) .build()) ``` ```java RxAmplify.addPlugin(AWSDataStorePlugin.builder().dataStoreConfiguration( DataStoreConfiguration.builder() .syncExpression(Post.class, () -> Post.RATING.gt(5)) .syncExpression(Comment.class, () -> Comment.STATUS.eq("active")) .build()) .build()); ``` When DataStore starts syncing, only Posts with `rating > 5` and Comments with `status` equal to `active` will be synced down to the user's local store. Developers should only specify a single `syncExpression` per model. Any subsequent expressions for the same model will be ignored. ### Reevaluate expressions at runtime Sync expressions get evaluated whenever DataStore starts. In order to have your expressions reevaluated, you can execute `Amplify.DataStore.clear()` or `Amplify.DataStore.stop()` followed by `Amplify.DataStore.start()`. If you have the following expression and you want to change the filter that gets applied at runtime, you can do the following: ```java public Integer rating = 5; public void initialize() { Amplify.addPlugin(AWSDataStorePlugin.builder().dataStoreConfiguration( DataStoreConfiguration.builder() .syncExpression(Post.class, () -> Post.RATING.gt(rating)) .build()) .build()); } public void changeSync() { rating = 1; Amplify.DataStore.stop( () -> Amplify.DataStore.start( () -> Log.i("MyAmplifyApp", "DataStore started"), error -> Log.e("MyAmplifyApp", "Error starting DataStore: ", error) ), error -> Log.e("MyAmplifyApp", "Error stopping DataStore: ", error) ); } ``` ```kotlin var rating: Int = 5; fun initialize() { Amplify.addPlugin(AWSDataStorePlugin.builder().dataStoreConfiguration( DataStoreConfiguration.builder() .syncExpression(Post::class.java) { Post.RATING.gt(rating) } .build()) .build()) } fun changeSync() { rating = 1; Amplify.DataStore.stop( { Amplify.DataStore.start( { Log.i("MyAmplifyApp", "DataStore started") }, { Log.e("MyAmplifyApp", "Error starting DataStore", it) } ) }, { Log.e("MyAmplifyApp", "Error stopping DataStore", it) } ) } ``` ```kotlin var rating = 5 fun initialize() { Amplify.addPlugin(AWSDataStorePlugin.builder().dataStoreConfiguration( DataStoreConfiguration.builder() .syncExpression(Post::class.java) { Post.RATING.gt(rating) } .build()) .build()) } suspend fun changeSync() { rating = 1 try { Amplify.DataStore.stop() Amplify.DataStore.start() Log.i("MyAmplifyApp", "DataStore started") } catch (error: DataStoreException) { Log.w("MyAmplifyApp", "Failed to restart DataStore", error) } } ``` ```java public Integer rating = 5; public void initialize() { RxAmplify.addPlugin(AWSDataStorePlugin.builder().dataStoreConfiguration( DataStoreConfiguration.builder() .syncExpression(Post.class, () -> Post.RATING.gt(rating)) .build()) .build()); } public void changeSync() { rating = 1; RxAmplify.DataStore.stop() .andThen(RxAmplify.DataStore.start()) .subscribe( () -> Log.i("MyAmplifyApp", "DataStore restarted"), error -> Log.e("MyAmplifyApp", "Error restarting DataStore: ", error) ); } ``` Each time DataStore starts (via `start` or any other operation: `query`, `save`, `delete`, or `observe`), DataStore will reevaluate the `syncExpressions`. In the above case, the predicate will contain the value `1`, so all Posts with `rating > 1` will get synced down. Keep in mind: `Amplify.DataStore.stop()` will retain the local store's existing content. Run `Amplify.DataStore.clear()` to clear the locally-stored contents. When applying a more restrictive filter, clear the local records first by running `DataStore.clear()` instead: ```java public void changeSync() { rating = 8; Amplify.DataStore.clear( () -> Amplify.DataStore.start( () -> Log.i("MyAmplifyApp", "DataStore started"), error -> Log.e("MyAmplifyApp", "Error starting DataStore: ", error) ), error -> Log.e("MyAmplifyApp", "Error clearing DataStore: ", error) ); } ``` ```kotlin fun changeSync() { rating = 8; Amplify.DataStore.clear( { Amplify.DataStore.start( { Log.i("MyAmplifyApp", "DataStore started") }, { Log.e("MyAmplifyApp", "Error starting DataStore", it) } ) }, { Log.e("MyAmplifyApp", "Error clearing DataStore", it) } ) } ``` ```kotlin suspend fun changeSync() { rating = 8 try { Amplify.DataStore.clear() Amplify.DataStore.start() Log.i("MyAmplifyApp", "DataStore started") } catch (error: DataStoreException) { Log.w("MyAmplifyApp", "Error clearing/starting DataStore", error) } } ``` ```java public void changeSync() { rating = 8; RxAmplify.DataStore.clear() .andThen(RxAmplify.DataStore.start() .subscribe( () -> Log.i("MyAmplifyApp", "DataStore cleared and restarted"), error -> Log.e("MyAmplifyApp", "Error clearing or restarting DataStore: ", error) ); } ``` This will clear the contents of your local store, reevaluate your sync expressions and re-sync the data from the cloud, applying all of the specified predicates to the sync queries. You can also have your sync expression return `QueryPredicates.all()` in order to remove any filtering for that model. This will have the same effect as the default sync behavior. ```java public Integer rating = null; public void initialize() { Amplify.addPlugin(AWSDataStorePlugin.builder().dataStoreConfiguration( DataStoreConfiguration.builder() .syncExpression(Post.class, () -> { if (rating != null) { return () -> Post.RATING.gt(rating); } return QueryPredicates.all(); }) .build()) .build()); } ``` ```kotlin var rating = null fun initialize() { Amplify.addPlugin(AWSDataStorePlugin.builder().dataStoreConfiguration( DataStoreConfiguration.builder() .syncExpression(Post::class.java) { if (rating != null) { Post.RATING.gt(rating) } QueryPredicates.all() } .build()) .build()) } ``` ```java public Integer rating = null; public void initialize() { RxAmplify.addPlugin(AWSDataStorePlugin.builder().dataStoreConfiguration( DataStoreConfiguration.builder() .syncExpression(Post.class, () -> { if (rating != null) { return () -> Post.RATING.gt(rating); } return QueryPredicates.all(); }) .build()) .build()); } ``` `DataStore.configure()` should only by called once. ### Advanced use case - Query instead of Scan You can configure selective sync to retrieve items from DynamoDB with a [query operation](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Query.html) against a GSI. By default, the base sync will perform a [scan](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Scan.html). Query operations enable a highly efficient and cost-effective data retrieval for customers running DynamoDB at scale. Learn about creating GSIs with the `@index` directive [here](https://docs.amplify.aws/cli/graphql/data-modeling). In order to do that, your `syncExpression` should return a predicate that maps to a query expression. For example, for the following schema: ```graphql type User @model { id: ID! firstName: String! lastName: String! @index(name: "byLastName", sortKeyFields: ["createdAt"]) createdAt: AWSDateTime! } ``` To construct a query expression, return a predicate with the primary key of the GSI. You can only use the `eq` operator with this predicate. For the schema defined above `User.LAST_NAME.eq("Doe")` is a valid query expression. Optionally, you can also chain the sort key to this expression, using any of the following operators: `eq | ne | le | lt | ge | gt | beginsWith | between`. E.g., `User.LAST_NAME.eq("Doe").and(User.CREATED_AT.gt("2020-10-10")`. Both of these sync expressions will result in AWS AppSync retrieving records from Amazon DynamoDB via a query operation: ```java Amplify.addPlugin(AWSDataStorePlugin.builder().dataStoreConfiguration( DataStoreConfiguration.builder() .syncExpression(User.class, () -> User.LAST_NAME.eq("Doe")) .build()) .build()); // OR Amplify.addPlugin(AWSDataStorePlugin.builder().dataStoreConfiguration( DataStoreConfiguration.builder() .syncExpression(User.class, () -> User.LAST_NAME.eq("Doe").and(User.CREATED_AT.gt("2020-10-10"))) .build()) .build()); ``` ```kotlin Amplify.addPlugin(AWSDataStorePlugin.builder().dataStoreConfiguration( DataStoreConfiguration.builder() .syncExpression(User::class.java) { User.LAST_NAME.eq("Doe") } .build()) .build()) // OR Amplify.addPlugin(AWSDataStorePlugin.builder().dataStoreConfiguration( DataStoreConfiguration.builder() .syncExpression(User::class.java) { User.LAST_NAME.eq("Doe").and(User.CREATED_AT.gt("2020-10-10")) } .build()) .build()) ``` ```java RxAmplify.addPlugin(AWSDataStorePlugin.builder().dataStoreConfiguration( DataStoreConfiguration.builder() .syncExpression(User.class, () -> User.LAST_NAME.eq("Doe")) .build()) .build()); // OR RxAmplify.addPlugin(AWSDataStorePlugin.builder().dataStoreConfiguration( DataStoreConfiguration.builder() .syncExpression(User.class, () -> User.LAST_NAME.eq("Doe").and(User.CREATED_AT.gt("2020-10-10"))) .build()) .build()); ```