Query DynamoDB by GSI using AppSync SDK and Amplify CLI

1.1k Views Asked by At

I'm trying to query a DynamoDB table through AWS AppSync, using AWS' Android AppSync SDK, and the Amplify CLI. I'm querying a Global Secondary Index (GSI). I'm getting an error:

Expression block '$[query]' requires an expression

I've found a similar issue which suggests the fix below. However, it doesn't work for me:

My schema.graphql is:

type olatexOrders @model
@key(fields: ["PK", "SK"])
@key(name: "GSI-item-1", fields: ["GSIPKitem1", "GSISKitem1" ], queryField: "olatexOrdersByGSIPKitem1AndGSISKitem1")
@key(name: "GSI-order-1", fields: ["GSIPKorder1", "GSISKorder1" ], queryField: "olatexOrdersByGSIPKorder1AndGSISKorder1")
{
    PK: String!
    SK: String!
    GSIPKitem1: String
    GSISKitem1: String
    GSIPKorder1: String
    GSISKorder1: String
    ... and many more fields not relevant for this case
}

The hash key (primary key + secondary key) and both GSIs (GSI-item-1, GSI-order1) were created correctly in DynamoDB. I'm also able to query my DynamoDB table from AppSync using GSI:

query MyQuery {
  olatexOrdersByGSIPKorder1AndGSISKorder1(GSIPKorder1: "IN_PROGRESS") {
    nextToken
    items {
      GSIPKorder1
      GSISKorder1
    }
  }
}

However, it doesn't work when I try to use autogenerated amplify classes inside my Android app, as below:

private void query() {
    AwsClientFactory.getInstance(context)
        .query(OlatexOrdersByGsipKorder1AndGsisKorder1Query
            .builder()
            .gSIPKorder1(ORDER_STATUS_IN_PROGRESS)
            .limit(200)
            .build())
        .responseFetcher(AppSyncResponseFetchers.NETWORK_ONLY)
        .enqueue(callback);
}

I'm getting the same error mentioned above. After reading the related issue, my understanding is that there is some bug/inconsistency/limitation in the way GSIs are implemented in AppSync and for that reason you need to specify not only the primary key of the GSI but the sort key and sort order as well. With this knowledge, for testing, I've rewrite my function to:

private void query() {
    AwsClientFactory.getInstance(context)
        .query(OlatexOrdersByGsipKorder1AndGsisKorder1Query
            .builder()
            .gSIPKorder1(ORDER_STATUS_IN_PROGRESS)
            .gSISKorder1(ModelStringKeyConditionInput.builder().beginsWith("K").build())
            .sortDirection(ModelSortDirection.DESC)
            .limit(200)
            .build())
        .responseFetcher(AppSyncResponseFetchers.NETWORK_ONLY)
        .enqueue(callback);
}

Unfortunately, I'm still getting same error:

Expression block '$[query]' requires an expression

I'm using Amplify CLI version 4.27.2. All help will be appreciated!

EDIT 1
I've tried to simplified my case. I've created GSI having only one column. Please see schema.graphql below:

type olatexOrders @model
@key(fields: ["PK", "SK"])
@key(name: "GSI-item-1", fields: ["GSIPKitem1"], queryField: "olatexOrdersByGSIItem")
@key(name: "GSI-order-1", fields: ["GSIPKorder1"], queryField: "olatexOrdersByGSIOrder")
{
    PK: String!
    SK: String!
    GSIPKitem1: String
    GSIPKorder1: String
    ... and many more fields not relevant for this case
}

Now I'm trying below code to query my dynamo table via Amplify & AppSync:

public class GetInProgressOrders {

    private GraphQLCall.Callback<OlatexOrdersByGsiOrderQuery.Data> callback = new GraphQLCall.Callback<OlatexOrdersByGsiOrderQuery.Data>() {
        @Override
        public void onResponse(@Nonnull Response<OlatexOrdersByGsiOrderQuery.Data> response) {
            try{
                Log.d("MyTest", "TST response error: "+errors.get(0).message());
            }
            catch (Exception e){
                Log.e("MyTest", e.getMessage())
            }
        }

        @Override
        public void onFailure(@Nonnull ApolloException e) {
            Log.e("MyTest", e.getMessage())
        }
    };

    private void query(Context context){
        AWSAppSyncClient awsClient = AwsClientFactory.getInstance(context);
        
        OlatexOrdersByGsiOrderQuery tmpQuery = OlatexOrdersByGsiOrderQuery
                .builder()
                .gSIPKorder1(ORDER_STATUS_IN_PROGRESS)
                .build();

        awsClient.query(
                tmpQuery
            )
                .responseFetcher(AppSyncResponseFetchers.NETWORK_ONLY)
                .enqueue(callback);
    }
}

Executing above ends up with same error as previously:

TST response error: Expression block '$[query]' requires an expression

That gives me feeling that I'm doing something significantly wrong. Basically I'm unable to query table in Amplify via GSI. Unfortunately I don't see my mistake.
Regards

1

There are 1 best solutions below

0
On

That will not exactly be the answer but the workaround that actually works.
It turned out, I had multiple issues, some documented better, some worse. Things I did to fix my codes:

  1. For communication from Android App to AWS AppSync use Amplify class instead using AWSAppSyncClient class.
  2. In schema.graphql don't use fields with capital letters only (in my case, instead using PK & SK use pk & sk)
  3. In schema.graphql don't create types that starts with lowercase
  4. Create id column of ID! type (even if you don't need it at all)

With all above being said, see my schema.graphql that acctually works:
type OlatexOrders @model

@key(fields: ["pk", "sk"])
@key(name: "GSI-item-1", fields: ["gsi_pk_item_1", "gsi_sk_item_1"], queryField: "olatexOrdersByGSIItem")
@key(name: "GSI-order-1", fields: ["gsi_pk_order_1", "gsi_sk_order_1"], queryField: "olatexOrdersByGSIOrder")
{
    pk: String!
    sk: String!
    gsi_pk_item_1: String
    gsi_sk_item_1: String
    gsi_pk_order_1: String
    gsi_sk_order_1: String
    ... different not relevant fields
    id: ID!
}

And my query in Android App:

Amplify.API.query(
ModelQuery.list(OlatexOrders.class, OlatexOrders.GSI_PK_ORDER_1.eq(ORDER_STATUS_IN_PROGRESS)),
                response -> {
                    if(response.hasErrors())
                        Log.i("TestTag", "Errors: " + response.getErrors().get(0));

                    if(response.hasData()){
                        for(OlatexOrders orders: response.getData()){
                            inProgressOrdersNames.add(orders.getGsiSkOrder_1());
                        }
                    }
                    else{
                        Log.i("TestTag", "No data");
                    }
                },
                error -> Log.e("TestTag", "Error", error)
        );

Regards!