Steve Mansfield
Steve Mansfield

Reputation: 371

Query with DynamoDB Secondary Index AWS SDK 2 Java exception creating DynamoDbIndex object

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]]1

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());

Upvotes: 10

Views: 20372

Answers (5)

vs_slavchev
vs_slavchev

Reputation: 46

Make sure your DynamoDbBean has correct getters and setters for the GSIPK and GSISK.

Confirm your index is correctly defined by debugging the result of TableSchema.fromBean(YourBean.class) and inspecting inside the delegates -> tableMetadata -> indexByNameMap should have your index name.

Upvotes: 1

smac2020
smac2020

Reputation: 10734

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);
        }
    }

Upvotes: 11

Andres Bores
Andres Bores

Reputation: 764

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;
}

Upvotes: 5

Steve Mansfield
Steve Mansfield

Reputation: 371

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; }

Upvotes: 17

jarvo69
jarvo69

Reputation: 8359

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());
        });

Upvotes: 0

Related Questions