0
votes

Similar to: I cannot query my dynamodb table from aws lambda due to wrong filterexpression? and DynamoDB update error Invalid UpdateExpression: An expression attribute value used in expression is not defined

I am trying to code a way to query DynamoDB tables using partial matches on Partition Key / Sort Key in Java.

The DynamoDB table I am trying to access has a Partition key of "Type" (A restricted key word in DynamoDB, I know, but not my choice) and a Sort key of "Id". I know the "Type" but not the full Id, so I have researched the Query method using AWS SDK 2.x source code and have implemented as shown below:

DynamoDBClient dynamoDbClient = DynamoDbClient.builder()
  .region(Region.EU_WEST_1)
  .credentialsProvider(StaticCredentialsProvider.create(awsCredentials))
  .build();

String idKey = "wholeIdKey";
String idValue = "partialIdValue";
String typeValue = "typeValue";

Map<String, String> expressionNames = new HashMap<>();
expressionNames.put("#t", "Type");

QueryRequest request = QueryRequest.builder()
  .tableName(tableName)
  .keyConditionExpression("begins_with ( " + idKey + ", :" + idValue + " )
     AND #t = :" + typeValue)
  .expressionAttributeNames(expressionNames)
  .build();

QueryResponse response = dynamoDbClient.query(request);

However, when I run this code, I get the following error message:

Exception in thread "main" software.amazon.awssdk.services.dynamodb.model.DynamoDbException: 
Invalid KeyConditionExpression: An expression attribute value used in expression is not defined; attribute value: :typeValue

It's as if it's not recognizing the fact that I have told the code use the Expression Attribute Names feature to replace the "#t" with "Type" (Which is a reserved keyword in DynamoDB)

Can anyone help?

EDIT: References for code:

https://docs.aws.amazon.com/code-samples/latest/catalog/javav2-dynamodb-src-main-java-com-example-dynamodb-Query.java.html

https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.ExpressionAttributeNames.html

https://www.javadoc.io/static/software.amazon.awssdk/dynamodb/2.7.14/software/amazon/awssdk/services/dynamodb/model/QueryRequest.html#expressionAttributeNames--

3

3 Answers

1
votes

Your expressionAttributeName looks fine, but you forgot to provide a value for :typeValue so dynamoDB cannot know what to look for.

In addition to what you did, you need to add an expressionAttributeValue where you can provide proper values. See documentation here

1
votes

The name is fine, but you're prefixing both values with ':'. That causes a lookup in ExpressionAttributeValues, which you did not provide.

Never try to write dynamic values directly into the query string.

0
votes

Fixed Code for whoever wants it in the future (Thanks to @aherve and @MattTimmermans)

DynamoDBClient dynamoDbClient = DynamoDbClient.builder()
  .region(Region.EU_WEST_1)
  .credentialsProvider(StaticCredentialsProvider.create(awsCredentials))
  .build();

String idKey = "wholeIdKey";
String idValue = "partialIdValue";
String typeValue = "typeValue";
String typeKey = "typeKey";

Map<String, String> expressionNames = new HashMap<>();
expressionNames.put("#t", "Type");
expressionNames.put("#i", "Id");

Map<String, AttributeValue> expressionValues = new HashMap<>();
expressionValues.put(":typeName", AttributeValue.builder().s(typeValue).build());
expressionValues.put(":idName", AttributeValue.builder().s(idValue).build());

QueryRequest request = QueryRequest.builder()
  .tableName(tableName)
  .keyConditionExpression("#t = :typeName AND begins_with ( #i, :idName )")
  .expressionAttributeNames(expressionNames)
  .expressionAttributeValues(expressionValues)
  .build();

response = dynamoDbClient.query(request);