0
votes

What is the current method using DynamoDBmapper and DynamoDBQueryExpression to query a global secondary index, sort on Sort Index, and return top n results?

First tried solution shown here: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DynamoDBMapper.Methods.html#DynamoDBMapper.Methods.query

However, withKeyConditionExpression no longer exists for DynamoDBQueryExpression. Next, I tried the example shown here: How to query a Dynamo DB having a GSI with only hashKeys using DynamoDBMapper

Which uses setHashKeyValues, but I get the following error: com.amazonaws.mobileconnectors.dynamodbv2.dynamodbmapper.DynamoDBMappingException: Public, zero-parameter hash key property must be annotated with interface com.amazonaws.mobileconnectors.dynamodbv2.dynamodbmapper.DynamoDBHashKey

If i do a scan, I am able to pull results, so I think permissions are correct:

                Map<String, AttributeValue> eav = new HashMap<String, AttributeValue>();
                eav.put(":val1", new AttributeValue().withS(Leaderboard));

                DynamoDBScanExpression scanExpression = new DynamoDBScanExpression()
                        .withFilterExpression("Leaderboard = :val1") // and Ranking <= :val2")
                        .withExpressionAttributeValues(eav);

DynamoDBmapper object looks like this. I didn't include any of the primary key attributes from the main table, just this subset.

@DynamoDBTable(tableName = "Users")

public class DynamoLeaderboard implements Serializable {

    private String leaderboard;
    private String userName;
    private int ranking;

    public DynamoLeaderboard() {}

    @DynamoDBIndexHashKey(globalSecondaryIndexName = "Leaderboard-Ranking-index", attributeName = "Leaderboard")
    public String getLeaderboard() {
        return leaderboard;
    }
    public void setLeaderboard(final String leaderboard) {
        this.leaderboard = leaderboard;
    }

    @DynamoDBIndexRangeKey(globalSecondaryIndexName = "Leaderboard-Ranking-index", attributeName = "Ranking")
    public int getRanking() {
        return ranking;
    }
    public void setRanking(int ranking) {
        this.ranking = ranking;
    }

    @DynamoDBAttribute(attributeName = "UserName")
    public String getUserName() {
        return userName;
    }
    public void setUserName(String userName) {
        this.userName = userName;
    }
}

Then the query is called:

final DynamoLeaderboard tmpLeaderboard = new DynamoLeaderboard();
tmpLeaderboard.setLeaderboard("0");

Map<String, AttributeValue> eav = new HashMap<String, AttributeValue>();
eav.put(":val1", new AttributeValue().withS("0"));

DynamoDBQueryExpression<DynamoLeaderboard> queryExpression = new DynamoDBQueryExpression<DynamoLeaderboard>();
queryExpression.withIndexName("Leaderboard-index");
queryExpression.setHashKeyValues(tmpLeaderboard);
queryExpression.withConsistentRead(false);

scanResult = mapper.query(DynamoLeaderboard.class, queryExpression);

Comments on what's causing the zero-property hash key exception? Does it have to do with setting the table main hash key vs the GSI hash key?

Recommendations on the current and correct way to execute queries on GSI?

Much appreciated!

2
Hey, did the answer end up working for you? If it did, you should accept it so that other SO users know that it solves the problem. If you had to tweak a few things or if you used a different solution, leave a comment or add your own answer to share that knowledge with others.Matthew Pope
@MatthewPope, that worked, thanks and sorry for the late accept!Jimbro

2 Answers

0
votes

You need to add the key attributes of the main table to your model. DynamoDBMapper is balking because the @DynamoDBTable annotated class has no @DynamoDBHashKey attribute.

Alternatively, you could try changing the @DynamoDBTable to @DynamoDBDocument, though I am less sure that will work.

1
votes

this is my way :

final Example example = new Example();
example.setID(senderAcid);

final DynamoDBQueryExpression<Example> queryExpression = new DynamoDBQueryExpression<>();

queryExpression.withIndexName("SomeGSIndex")
    .withConsistentRead(false)
    .withProjectionExpression("id,omeOtherData,someOtherFieldName")
    .withHashKeyValues(example);

---------- important condition for SortKey

queryExpression.withRangeKeyCondition("sortKeyField",
    new Condition()
        .withComparisonOperator(ComparisonOperator.EQ)
        .withAttributeValueList(new AttributeValue().withS(sortKeyValue)));

-------- finally

mapper.query(Example.class, query);