For some `list` methods in Merchant API, such as
[account.list](https://developers.google.com/merchant/api/reference/rest/accounts_v1/accounts/list)
you can perform filtering so you only get the results you are interested in.
To filter results, you use the `filter` parameter with the syntax defined in the
[EBNF grammar](https://google.aip.dev/assets/misc/ebnf-filtering.txt).
This page explains how to use the syntax to
[filter accounts](https://developers.google.com/merchant/api/guides/accounts/filter).

## Syntax

All values other than integers must be enclosed in double quotes ("). To learn
what values a specific field accepts, see the [reference
documentation](https://developers.google.com/merchant/api/reference/rest) for that field.

You can use `AND` to filter for multiple fields in the same query. You can also
use `AND` to combine multiple `relationship(...)` and `service(...)` filters.
Here's an example that combines multiple `relationship(...)` and `service(...)`
filters:

    (relationship(service(type = "ACCOUNT_MANAGEMENT") AND service(handshakeState = "PENDING"))) OR (accountName = "store" AND relationship(...))

This example returns the following accounts:

- All accounts with an account management relationship to another account, and
  an additional relationship that's pending acceptance.

- All accounts with the display name `"store"`, who have relationships to
  other accounts.

You can't use `AND` to filter for multiple values in the same field. For
example, you can't use `accountName = "*A*" AND accountName = "*B*"`.

You can use `OR` to filter for two fields in the same query. Enclose the filter
criteria on each side of the `OR` operator with parentheses. For example,
`(accountName = "storeA") OR (accountName = "storeB")`.

You can only use `OR` to combine two fields. For example, you can't use
`(accountName = "storeA") OR (accountName = "storeB") OR (accountName =
"storeC")`.

Parentheses aren't allowed other than with the `AND` and `OR` operators, and in
function invocations, like `relationship(...)` and `service(...)`.

For string fields like `accountName` and `accountIdAlias`, you can filter for
values that contain a certain word or sequence of characters by enclosing the
sequence in asterisks (`*`). For example, `accountName = "*foo*"` returns all
accounts with an `accountName` containing `foo`, like "storeFoo".

You can filter for values that don't contain a certain sequence by using `!=`
and `*`. For example, `accountName != "*foo*"` returns all accounts with an
`accountName` that doesn't contain `foo`.

> [!NOTE]
> **Note:** that filtering by `accountIdAlias` or `externalAccountId` is only possible if a `providerId` is also set as these fields. Using these fields only makes sense in the context of a specific `providerId`.

Extra white spaces are ignored. For example, `foo AND bar` is the same as `foo
AND bar`.

Here are a few examples of filtering accounts using the
[account.list](https://developers.google.com/merchant/api/reference/rest/accounts_v1/accounts/list) method:

- All sub-accounts of an advanced account containing "Store":

    accountName = "*store*" AND relationship(service(type = "ACCOUNT_AGGREGATION"))

- All accounts that are managed by provider 123456:

    relationship(service(type = "ACCOUNT_MANAGEMENT") AND providerId = 123456)

- All accounts that have sent an invitation to provider 123456 or need to accept an invitation from this provider:

    relationship(service(handshakeState = "PENDING" AND type ="ACCOUNT_MANAGEMENT")
    AND providerId = 123456)

To filter for the accounts the user making the request can access, use the
[`google.shopping.merchant.accounts.v1.ListAccountsRequest`](https://developers.google.com/merchant/api/reference/rpc/google.shopping.merchant.accounts.v1#google.shopping.merchant.accounts.v1.ListAccountsRequest) method, as shown in the following sample.

### Java

    import com.google.api.gax.core.FixedCredentialsProvider;
    import com.google.auth.oauth2.GoogleCredentials;
    import com.google.shopping.merchant.accounts.v1.Account;
    import com.google.shopping.merchant.accounts.v1.AccountsServiceClient;
    import com.google.shopping.merchant.accounts.v1.AccountsServiceClient.ListAccountsPagedResponse;
    import com.google.shopping.merchant.accounts.v1.AccountsServiceSettings;
    import com.google.shopping.merchant.accounts.v1.ListAccountsRequest;
    import shopping.merchant.samples.utils.Authenticator;
    import shopping.merchant.samples.utils.Config;

    /** This class demonstrates how to filter the accounts the user making the request has access to. */
    public class FilterAccountsSample {

      public static void filterAccounts(Config config) throws Exception {

        // Obtains OAuth token based on the user's configuration.
        GoogleCredentials credential = new Authenticator().authenticate();

        // Creates service settings using the credentials retrieved above.
        AccountsServiceSettings accountsServiceSettings =
            AccountsServiceSettings.newBuilder()
                .setCredentialsProvider(FixedCredentialsProvider.create(credential))
                .build();

        // Calls the API and catches and prints any network failures/errors.
        try (AccountsServiceClient accountsServiceClient =
            AccountsServiceClient.create(accountsServiceSettings)) {

          // Filter for accounts with display names containing "store" and a provider with the ID "123":
          String filter = "accountName = \"*store*\" AND relationship(providerId = 123)";

          // Filter for all subaccounts of account "123":
          // String filter2 = "relationship(providerId = 123 AND service(type =
          // \"ACCOUNT_AGGREGATION\"))";

          // String filter3 = "relationship(service(handshakeState = \"APPROVED\" AND type =
          // \"ACCOUNT_MANAGEMENT\") AND providerId = 123)";

          ListAccountsRequest request = ListAccountsRequest.newBuilder().setFilter(filter).build();

          System.out.println("Sending list accounts request with filter:");
          ListAccountsPagedResponse response = accountsServiceClient.listAccounts(request);

          int count = 0;

          // Iterates over all rows in all pages and prints the sub-account
          // in each row.
          // `response.iterateAll()` automatically uses the `nextPageToken` and recalls the
          // request to fetch all pages of data.
          for (Account account : response.iterateAll()) {
            System.out.println(account);
            count++;
          }
          System.out.print("The following count of elements were returned: ");
          System.out.println(count);
        } catch (Exception e) {
          System.out.println(e);
        }
      }

      public static void main(String[] args) throws Exception {
        Config config = Config.load();

        filterAccounts(config);
      }
    }

## Specification

The filters follow a subset of the [AIP filter](https://google.aip.dev/160)
specification, and its [formal EBNF
grammar](https://google.aip.dev/assets/misc/ebnf-filtering.txt):

    filter
        : accountFilterDisj
        | accountFilterConj

    accountFilterDisj
        : "(" accountFilterConj " OR " accountFilterConj ")"
        ;
    accountFilterConj
        : accountFilter {" AND " accountFilter}
        ;

    accountFilter
        : accountNameFilter | capabilityFilter | relationshipFn | accessFilter
        ;

    accountNameFilter
        : "accountName" comparator value
        ;

    capabilityFilter
        : "capabilities:" capabilityValue
        | "-capabilities:" capabilityValue
        | "NOT capabilities:" capabilityValue
        ;
    capabilityValue
        : "CAN_UPLOAD_PRODUCTS"
        ;

    accessFilter
        : "access = " accessType
        ;

    accessType
        : "DIRECT"
        | "INDIRECT"
        | "ALL"
        ;

    relationshipFn
        : "relationship(" relationshipConj ")"
        ;
    relationshipConj
        : relationshipFilter {" AND " relationshipFilter}
        ;
    relationshipFilter
        : "providerId = " numValue
        | "accountIdAlias" comparator value
        | serviceFn
        ;

    serviceFn
        : "service(" serviceConj ")"
        ;
    serviceConj
        : serviceFilter {" AND " serviceFilter}
        ;
    serviceFilter
        : "externalAccountId" comparator value
        | "handshakeState = " handshakeState
        | "type = " serviceType
        ;

    handshakeState
        : "PENDING"
        | "WAITING"
        | "ESTABLISHED"
        | "REJECTED"
        ;
    serviceType
        : "ACCOUNT_AGGREGATION"
        | "ACCOUNT_MANAGEMENT"
        ;

    comparator
        : " = "
        | " != "
        ;