0

I have a DynamoDb Table. It has a hash key of "ID" and range key of "Category".

I am trying to find all entries for a "Category", comedy for example.

This is my mapping class:

@DynamoDBTable(tableName = "Videos")
public class VideoDynamoMappingAdapter {

    private String videoID;
    private String videoCategory;
    private String videoArtist;
    private String videoTitle;

    @DynamoDBHashKey(attributeName = "ID")
    public String getVideoID() {
        return videoID;
    }

    public void setVideoID(String videoID) {
        this.videoID = videoID;
    }
    @DynamoDBRangeKey(attributeName = "Category")
    public String getVideoCategory() {
        return videoCategory;
    }

    public void setVideoCategory(String videoCategory) {
        this.videoCategory = videoCategory;
    }

    @DynamoDBAttribute(attributeName = "ArtistName")
    public String getVideoArtist() {
        return videoArtist;
    }

    public void setVideoArtist(String videoArtist) {
        this.videoArtist = videoArtist;
    }

    @DynamoDBAttribute(attributeName = "VideoTitle")
    public String getVideoTitle() {
        return videoTitle;
    }

    public void setVideoTitle(String videoTitle) {
        this.videoTitle = videoTitle;
    }

}

I have been trying to query something like this:

    DynamoDBQueryExpression<VideoDynamoMappingAdapter> queryExpression = new DynamoDBQueryExpression<VideoDynamoMappingAdapter>()
            .withRangeKeyCondition()


    List<VideoDynamoMappingAdapter> itemList = mapper.query(VideoDynamoMappingAdapter.class, queryExpression);

I have been looking over the guide but I can't understand it, I would have thought this would be an easy query that is done all the time so maybe I am missing something:

http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/QueryingJavaDocumentAPI.html#DocumentAPIJavaQueryExample

Thanks in advance for your help

2 Answers 2

3

Notionquest was right in his answer that I could not query for all values of a hash key in a given range condition. I solved this by creating a global secondary index on my Category attribute.

This is my mapper class:

@ DynamoDBTable(tableName = "AlarmTable")
public class VideoDynamoMappingAdapter {

    private String videoID;
    private String videoCategory;
    private String videoArtist;
    private String videoTitle;

    @DynamoDBHashKey(attributeName = "ID")
    public String getVideoID() {
        return videoID;
    }

    public void setVideoID(String videoID) {
        this.videoID = videoID;
    }

    @DynamoDBIndexHashKey( globalSecondaryIndexName = "CategoryIndex", attributeName = "Category")
    public String getVideoCategory() {
        return videoCategory;
    }

    public void setVideoCategory(String videoCategory) {
        this.videoCategory = videoCategory;
    }

    @DynamoDBAttribute(attributeName = "VideoArtist")
    public String getVideoArtist() {
        return videoArtist;
    }

    public void setVideoArtist(String videoArtist) {
        this.videoArtist = videoArtist;
    }

    @DynamoDBAttribute(attributeName = "VideoTitle")
    public String getVideoTitle() {
        return videoTitle;
    }

    public void setVideoTitle(String videoTitle) {
        this.videoTitle = videoTitle;
    }

}

Notice the annotation on category for that.

And then my query is like this:

VideoDynamoMappingAdapter videosToFind = new VideoDynamoMappingAdapter();
        videosToFind.setVideoCategory("Other");

        DynamoDBQueryExpression<VideoDynamoMappingAdapter> queryExpression = new DynamoDBQueryExpression<VideoDynamoMappingAdapter>()
                .withIndexName("CategoryIndex")
                .withHashKeyValues(videosToFind)
                .withConsistentRead(false);

        List<VideoDynamoMappingAdapter> itemList = mapper.query(VideoDynamoMappingAdapter.class, queryExpression);
        System.out.println("test" + itemList.size());

I hope it helps someone.

Sign up to request clarification or add additional context in comments.

Comments

2

The DynamoDB table can't be queried by Range key alone without the Hash key. However, it can be queried by Hash key alone without the Range key.

When the DynamoDB table is queried by Range key alone without the Hash key, it would throw ValidationException.

Unable to read item. Error JSON: {
  "message": "Query condition missed key schema element",
  "code": "ValidationException"
}

The alternate option would be to scan the database using the Range key. Please note that DynamoDB scan is a costly operation as it will scan the entire table.

Scanning the Video table by categories:-

Please note that "OR" condition is used as "IN" is supported for certain data types only.

public List<VideoDynamoMappingAdapter> getVidoesByCategory(String[] categories) {

        ScanResultPage<VideoDynamoMappingAdapter> videoResultPage = null;
        List<VideoDynamoMappingAdapter> videoList = new ArrayList<>();

        try {

            do {
                DynamoDBMapper dynamoDBMapper = new DynamoDBMapper(dynamoDBClient);

                Map<String, AttributeValue> expressionAttributeValues = new LinkedHashMap<String, AttributeValue>();

                StringBuilder filterExpression = new StringBuilder(); 
                int i = 1;

                for (String category : categories) {
                    if (i==1) {
                        filterExpression.append("Category = " + ":videoCategory" + i);  
                    } else {
                        filterExpression.append(" OR Category = " + ":videoCategory" + i);
                    }

                    expressionAttributeValues.put(":videoCategory" + i, new AttributeValue().withS(category));
                    i++;
                }

                System.out.println("Filterexpression =====>" + filterExpression.toString());

                DynamoDBScanExpression scanExpression = new DynamoDBScanExpression()                
                        .withFilterExpression(filterExpression.toString())
                        .withExpressionAttributeValues(expressionAttributeValues);

                if (videoResultPage != null) {
                    scanExpression.setExclusiveStartKey(videoResultPage.getLastEvaluatedKey());
                }

                videoResultPage = dynamoDBMapper.scanPage(VideoDynamoMappingAdapter.class, scanExpression);

                System.out.println("Results ==========>" + videoResultPage.getResults());

                videoList.addAll(videoResultPage.getResults());

            } while (videoResultPage.getLastEvaluatedKey() != null);

        } catch (Exception db) {

            db.printStackTrace();

        }

        System.out.println(videoList.toString());
        return videoList;

    }

5 Comments

Thanks for your response. Can you show me how to query all of the hashes in the range?
Added the scanning code. As mentioned in my earlier answer, query is not possible without hash key.
Thank you very much for response. It showed me that my understanding was incorrect for the documentation. I don't think scan everytime is a good option so I have changed it to add a global secondary index and I query on that.
May I request you to accept the answer both gsi and this? None of them is accepted at the moment.
I cannot accept two answers. As the question is how can I query. I will accept mine. I have upvoted yours. Thanks again for your help.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.