## Selectively syncing a subset of your data

By default, DataStore downloads the entire contents of your cloud data source to your local device.
The max number of records that will be stored is configurable [here](/lib-v1/datastore/conflict#custom-configuration).

You can utilize selective sync to only 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.

```dart
void _configureAmplify() async {
  // Update AmplifyDataStore instance like below
  final datastorePlugin = AmplifyDataStore(
    modelProvider: ModelProvider.instance,
    syncExpressions: [
      DataStoreSyncExpression(Post.classType, () => Post.RATING.gt(5)),
      DataStoreSyncExpression(
        Comment.classType,
        () => Comment.POST.beginsWith('This'),
      )
    ],
  );
  ...
}
```

When DataStore starts syncing, only Posts with `rating > 5` and Comments with `POST` id begins with `"This"` 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>

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

```dart
int rating = 5;

Future<void> initialize() async {
  final dataStorePlugin = AmplifyDataStore(
    modelProvider: ModelProvider.instance,
    syncExpressions: [
      DataStoreSyncExpression(
        Post.classType,
        () => Post.RATING.gt(rating),
      ),
    ],
  );

  await Amplify.addPlugin(dataStorePlugin);
}

Future<void> changeSync() async {
  rating = 1;
  try {
    await Amplify.DataStore.stop();
  } catch(error) {
    print('Error stopping DataStore: $error');
  }

  try {
    await Amplify.DataStore.start();
  } on Exception catch (error) {
    print('Error starting 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.

<Callout>

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

</Callout>

```dart
Future<void> changeSync() async {
  rating = 8;
  try {
    await Amplify.DataStore.clear();
  } on Exception catch (error) {
    print('Error clearing DataStore: $error');
  }

  try {
    await Amplify.DataStore.start();
  } on Exception catch (error) {
    print('Error starting 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 `QueryPredicateConstant.all` in order to remove any filtering for that model. This will have the same effect as the default sync behavior.

```dart
int rating = 5;

Future<void> initialize() async {
  var dataStorePlugin = AmplifyDataStore(
    modelProvider: ModelProvider.instance,
    syncExpressions: [
      DataStoreSyncExpression(
        Post.classType,
        () {
          if (rating > 0) {
            return QueryPredicate.all;
          }

          return Post.RATING.gt(rating);
        },
      ),
    ],
  );

  await Amplify.addPlugin(dataStorePlugin);
}
```

<Callout warning>

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

</Callout>

### Advanced use case - Query instead of Scan

By default, Datastore sync performs a [Scan](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Scan.html) on DynamoDB tables, which is [less efficient](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/bp-query-scan.html) when a table contains a large set of data. You can use `syncExpression` when configuring Datastore to filter synced data to improve performance using [Query](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Query.html) rather than Scan.

You can enable Query during the sync by following the below steps:

1. Create a [Global Secondary Index (GSI)](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/GSI.html) for your schema, e.g. in the below schema a GSI will be created using field `lastName` as the GSI primary key, and `createdAt` as the GSI sort key. Learn about creating GSIs with the `@index` directive [here](https://docs.amplify.aws/cli/graphql/data-modeling).

```graphql
type User @model {
  id: ID!
  firstName: String!
  lastName: String! @index(name: "byLastName", sortKeyFields: ["createdAt"])
  createdAt: AWSDateTime!
}
   ```

2. Configure DataStore with `syncExpression` returning a predicate that maps to a query expression.

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.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.LASTNAME.eq("Doe").and(User.CREATEDAT.gt("2020-10-10"))`.

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

```dart
Future<void> initializeSingleEquals() async {
  // Using eq operator with the primary key of the GSI
  final singleEqualsStore = AmplifyDataStore(
    modelProvider: ModelProvider.instance,
    syncExpressions: [
      DataStoreSyncExpression(
        User.classType,
        () => User.LASTNAME.eq("Doe"),
      ),
    ],
  );

  await Amplify.addPlugin(singleEqualsStore);
}

Future<void> initializeChainedEquals() async {
  // Using eq operator with the primary key of the GSI and
  // chaining the gt operator with the sort key
  final chainedEqualGtStore = AmplifyDataStore(
    modelProvider: ModelProvider.instance,
    syncExpressions: [
      DataStoreSyncExpression(
        User.classType,
        () => User.LASTNAME.eq("Doe").and(User.CREATEDAT.gt("2020-10-10")),
      ),
    ],
  );

  await Amplify.addPlugin(chainedEqualGtStore);
}
```
