9

I'm having trouble running a query against a secondary index, getting an exception:

Ex getting dynamodb scan: java.lang.IllegalArgumentException: Attempt to execute an operation that requires a secondary index without defining the index attributes in the table metadata. Index name: category-timestamp-index

Can someone guide me on how I'm doing this wrong?

My table is idIT_RSS_Sources and I've created an index category-timestamp-index.

screenshot attached of index

My code is:

DynamoDbEnhancedClient enhancedClient = getEnhancedDBClient(region);

 // Create a DynamoDbTable object

logger.debug("getting RSS Source category-timestamp-index");

//this throws the exception         
DynamoDbIndex<RSS_Source> catIndex = 
        enhancedClient.table("idIT_RSS_Sources", 
        TableSchema.fromBean(RSS_Source.class))
         .index("category-timestamp-index");


                logger.debug("building query attributes");

                AttributeValue att = AttributeValue.builder()
                        .s(theCategory)
                        .build();

                Map<String, AttributeValue> expressionValues = new HashMap<>();
                expressionValues.put(":value", att);

                Expression expression = Expression.builder()
                        .expression("category = :value")
                        .expressionValues(expressionValues)
                        .build();


                // Create a QueryConditional object that's used in the query operation
                QueryConditional queryConditional = QueryConditional
                        .keyEqualTo(Key.builder().partitionValue(theCategory)
                        .build());

                logger.debug("calling catIndex.query in getRSS...ForCategory");

                Iterator<Page<RSS_Source>> dbFeedResults =  (Iterator<Page<RSS_Source>>) catIndex.query(
                        QueryEnhancedRequest.builder()
                        .queryConditional(queryConditional)
                        .build());
Steve Mansfield
  • 341
  • 1
  • 2
  • 7

4 Answers4

15

solved, I was not using the proper annotation in my model class:

@DynamoDbSecondaryPartitionKey(indexNames = { "category-index" }) 
public String getCategory() { return category; } 
public void setCategory(String category) { this.category = category; }
Steve Mansfield
  • 341
  • 1
  • 2
  • 7
9

Assume you have a model named Issues.

package com.example.dynamodb;

import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbBean;
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbPartitionKey;
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbSecondaryPartitionKey;
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbSortKey;

@DynamoDbBean
public class Issues {

    private String issueId;
    private String title;
    private String createDate;
    private String description;
    private String dueDate;
    private String status;
    private String priority;
    private String lastUpdateDate;

    @DynamoDbPartitionKey
    public String getId() {

        return this.issueId;
    }

    public void setId(String id) {

        this.issueId = id;
    }

    @DynamoDbSortKey
    public String getTitle() {
        return this.title;
    }

    public void setTitle(String title) {

        this.title = title;
    }

    public void setLastUpdateDate(String lastUpdateDate) {

        this.lastUpdateDate = lastUpdateDate;
    }

    public String getLastUpdateDate() {
        return this.lastUpdateDate;
    }

    public void setPriority(String priority) {

        this.priority = priority;
    }

    public String getPriority() {
        return this.priority;
    }

    public void setStatus(String status) {

        this.status = status;
    }

    public String getStatus() {
        return this.status;
    }

    public void setDueDate(String dueDate) {

        this.dueDate = dueDate;
    }

    @DynamoDbSecondaryPartitionKey(indexNames = { "dueDateIndex" })
    public String getDueDate() {
        return this.dueDate;
    }


    public String getDate() {
        return this.createDate;
    }

    public void setDate(String date) {

        this.createDate = date;
    }

    public String getDescription() {
        return this.description;
    }

    public void setDescription(String description) {

        this.description = description;
    }
}

Notice the annotation on getDueDate.

@DynamoDbSecondaryPartitionKey(indexNames = { "dueDateIndex" })
    public String getDueDate() {
        return this.dueDate;
    }

This is because the Issues table has a secondary index named dueDateIndex.

enter image description here

To query on this secondary index, you can use this code that uses the Amazon DynamoDB Java API V2:

public static void queryIndex(DynamoDbClient ddb, String tableName, String indexName) {

        try {
            // Create a DynamoDbEnhancedClient and use the DynamoDbClient object
            DynamoDbEnhancedClient enhancedClient = DynamoDbEnhancedClient.builder()
                    .dynamoDbClient(ddb)
                    .build();

            //Create a DynamoDbTable object based on Issues
            DynamoDbTable<Issues> table = enhancedClient.table("Issues", TableSchema.fromBean(Issues.class));

            String dateVal = "2013-11-19";

            DynamoDbIndex<Issues> secIndex =
                    enhancedClient.table("Issues",
                            TableSchema.fromBean(Issues.class))
                           .index("dueDateIndex");


            AttributeValue attVal = AttributeValue.builder()
                    .s(dateVal)
                    .build();

             // Create a QueryConditional object that's used in the query operation
            QueryConditional queryConditional = QueryConditional
                    .keyEqualTo(Key.builder().partitionValue(attVal)
                            .build());

                // Get items in the Issues table
            SdkIterable<Page<Issues>> results =  secIndex.query(
                    QueryEnhancedRequest.builder()
                            .queryConditional(queryConditional)
                            .build());

            AtomicInteger atomicInteger = new AtomicInteger();
            atomicInteger.set(0);
            results.forEach(page -> {

                Issues issue = (Issues) page.items().get(atomicInteger.get());
                System.out.println("The issue title is "+issue.getTitle());
                atomicInteger.incrementAndGet();
            });


        } catch (DynamoDbException e) {
            System.err.println(e.getMessage());
            System.exit(1);
        }
    }
LEQADA
  • 1,913
  • 3
  • 22
  • 41
smac2020
  • 9,637
  • 4
  • 24
  • 38
4

For what it's worth, if your Global Secondary Index has a sort key, you must annotate that field in the DynamoDB bean with:

@DynamoDbSecondarySortKey(indexNames = { "<indexName>" })
public String getFieldName() {
   return fieldName;
}
Andres Bores
  • 212
  • 1
  • 14
0

My working code is as below: sortKey-index = GSI in dynamo db

List<Flow> flows = new ArrayList<>();
        DynamoDbIndex<Flow> flowBySortKey = table().index("sortKey-index");
        // Create a QueryConditional object that's used in the query operation
        QueryConditional queryConditional = QueryConditional
                        .keyEqualTo(Key.builder()
                        .partitionValue(sortKey)
                        .build());

        SdkIterable<Page<Flow>> dbFeedResults = flowBySortKey.query(
                        QueryEnhancedRequest.builder()
                        .queryConditional(queryConditional)
                        .build());

        dbFeedResults.forEach(flowPage -> {
            flows.addAll(flowPage.items());
        });
jarvo69
  • 7,908
  • 2
  • 18
  • 28
  • Thanks, can you share how your Flow class is defined? I'm still getting the original exception: Attempt to execute an operation that requires a secondary index without defining the index attributes in the table metadata. Index name: category-timestamp-index – Steve Mansfield May 29 '20 at 02:06
  • 1
    solved, I was not using the proper annotation in my model class: @DynamoDbSecondaryPartitionKey(indexNames = { "category-index" }) public String getCategory() { return category; } public void setCategory(String category) { this.category = category; } – Steve Mansfield May 29 '20 at 03:48