## 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.

<Callout>

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.

</Callout>

```swift
let syncExpr1 = DataStoreSyncExpression.syncExpression(Post.schema) {
    Post.keys.rating.gt(5)
}
let syncExpr2 = DataStoreSyncExpression.syncExpression(Comment.schema) {
    Comment.keys.status.eq("active")
}
try Amplify.add(plugin: AWSDataStorePlugin(modelRegistration: AmplifyModels(),
                                           configuration: .custom(
					    syncExpressions: [syncExpr1, syncExpr2])))
```

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.

<Callout>

Developers should only specify a single `syncExpression` per model. Any subsequent expressions for the same model will be ignored.

</Callout>

### Re-evaluate expressions at runtime
Sync expressions get evaluated whenever DataStore starts.
In order to have your expressions re-evaluated, 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:

```swift
public var rating = 5

func initialize() {
    let variableSyncExpr = DataStoreSyncExpression.syncExpression(Post.schema) {
        Post.keys.rating.gt(self.rating)
    }
    try Amplify.add(plugin: AWSDataStorePlugin(modelRegistration: AmplifyModels(),
                                               configuration: .custom(
                                                syncExpressions: [variableSyncExpr])))
}

func changeSync() {
    rating = 1
    Amplify.DataStore.stop { (result) in
        switch(result) {
        case .success:
            Amplify.DataStore.start { (result) in
                switch(result) {
                case .success:
                    print("DataStore started")
                case .failure(let error):
                    print("Error starting DataStore:\(error)")
                }
            }
        case .failure(let error):
            print("Error stopping DataStore:\(error)")
        }
    }      
}
```
Each time DataStore starts (via `start` or any other operation: `query`, `save`, `delete`, or `observe`), DataStore will re-evaluate 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.

<Callout>

When applying a more restrictive filter, clear the local records first by running `DataStore.clear()` instead:

</Callout>

```swift
func changeSync() {
    rating = 8
    Amplify.DataStore.clear { (result) in
        switch(result) {
        case .success:
            Amplify.DataStore.start { (result) in
                switch(result) {
                case .success:
                    print("DataStore started")
                case .failure(let error):
                    print("Error starting DataStore:\(error)")
                }
            }
        case .failure(let error):
            print("Error clearing DataStore:\(error)")
        }
    }    
```

This will clear the contents of your local store, re-evaluate 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 `QueryPredicateConstant.all` in order to remove any filtering for that model. This will have the same effect as the default sync behavior.

```swift
public var rating: Int? = 5

func initialize() {
    let syncExpr = DataStoreSyncExpression.syncExpression(Post.schema) {
        guard let rating = self.rating else {
            return QueryPredicateConstant.all
        }
        return Post.keys.rating.gt(rating)
    }
    try Amplify.add(plugin: AWSDataStorePlugin(modelRegistration: AmplifyModels(),
                                               configuration: .custom(
                                                syncExpressions: [syncExpr])))
```

<Callout warning>

`DataStore.configure()` should only by called once.

</Callout>

### 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.keys.lastName.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.keys.lastName.eq("Doe").and(User.keys.createdAt.gt("2020-10-10")`.

Both of these sync expressions will result in AWS AppSync retrieving records from Amazon DynamoDB via a query operation:

```swift
let syncExpr = DataStoreSyncExpression.syncExpression(User.schema) {
    User.keys.lastName.eq("Doe")
}
try Amplify.add(plugin: AWSDataStorePlugin(modelRegistration: AmplifyModels(),
                                           configuration: .custom(syncExpressions: [syncExpr])))
// OR

let syncExpr = DataStoreSyncExpression.syncExpression(User.schema) {
    User.keys.lastName.eq("Doe").and(User.keys.createdAt.gt("2020-10-10"))
}
try Amplify.add(plugin: AWSDataStorePlugin(modelRegistration: AmplifyModels(),
                                           configuration: .custom(syncExpressions: [syncExpr])))
```
