question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

The updateItem method does not work with String Set type

See original GitHub issue

I am unable to add item to a String Set type attribute.

Programming Language: Kotlin 1.3.72

Describe the issue

I used to use the standard DynamoDbAsyncClient in the Java SDK v2 to do an update item operation which has a String Set attribute. The update item expression was specified in this way.

val updateItemRequest: UpdateItemRequest = UpdateItemRequest
            .builder()	
            .tableName(tableName)	
            .key(	
                mapOf(	
                    "customerId" to AttributeValue.builder().s(eligibilityPersist.customerId).build()
                )	
            )
            .updateExpression(	
                "ADD #eligibility :eligibility " + "SET #eventDateTime = :eventDateTime, #eventId = :eventId"
            )
            .expressionAttributeNames(	
                mapOf(
                    "#eligibility" to "eligibility",
                    "#eventDateTime" to "eventDateTime",
                    "#eventId" to "eventId"
                )
            )	
            .expressionAttributeValues(	
                mapOf(	
                    ":eligibility" to AttributeValue.builder().ss(eligibilityPersist.eligibility.first()).build(),	
                    ":eventDateTime" to AttributeValue.builder().s(eligibilityPersist.eventDateTime).build(),	
                    ":eventId" to AttributeValue.builder().s(eligibilityPersist.eventId).build()
                )	
            )	
            .build()

It works as expected, i.e. if there’s already an existing eligibility attribute, the above update item request appends the new item (if not already in the String Set) to it. I am trying to replicate that using the async client in dynamodb-enhanced package. However, instead of adding it to the existing String Set, it overwrites it entirely.

Please see my code snippet below.

Steps to Reproduce

See this code snippet.

import reactor.core.publisher.Mono
import reactor.kotlin.core.publisher.toMono
import software.amazon.awssdk.enhanced.dynamodb.DynamoDbEnhancedAsyncClient
import software.amazon.awssdk.enhanced.dynamodb.Expression
import software.amazon.awssdk.enhanced.dynamodb.Key
import software.amazon.awssdk.enhanced.dynamodb.TableSchema
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbBean
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbPartitionKey
import software.amazon.awssdk.services.dynamodb.model.AttributeValue

@DynamoDbBean
data class EligibilityPersist(
    @get:DynamoDbPartitionKey var customerId: String? = null,
    var eligibility: MutableSet<String>? = null,
    var eventDateTime: String? = null,
    var eventId: String? = null
)

class CustomerEligibilityRepo(
    client: DynamoDbEnhancedAsyncClient,
    tableName: String
) {

    private val table = client.table(tableName, ELIGIBILITY_TABLE_SCHEMA)

    fun getEligibility(customerId: String): Mono<EligibilityPersist> {
        val key = Key.builder()
            .partitionValue(customerId)
            .build()

        return table.getItem(key).toMono()
    }

    fun updateWithExpression(eligibilityPersist: EligibilityPersist): Mono<EligibilityPersist> {
        return table
            .updateItem { r ->
                r.item(eligibilityPersist)
                    .ignoreNulls(true)
                    .conditionExpression(
                        Expression.builder()
                            .expression("ADD campaignEligibility = :campaignEligibility")
                            .putExpressionValue(":campaignEligibility", AttributeValue.builder().ss(eligibilityPersist.eligibility).build())
                            .build()
                    )
            }
            .toMono()
    }

    fun update(eligibilityPersist: EligibilityPersist): Mono<EligibilityPersist> {
        return table
            .updateItem { r ->
                r.item(eligibilityPersist).ignoreNulls(true)
            }
            .toMono()
    }

    companion object {
        private val ELIGIBILITY_TABLE_SCHEMA = TableSchema.fromBean(EligibilityPersist::class.java)
    }
}

Then, just execute any of the update functions.

Current Behavior

So, the updateWithExpression() does not work with the following error.

"stack_trace":"com.amazonaws.services.dynamodbv2.exceptions.DynamoDBLocalServiceException: Invalid ConditionExpression: Syntax error; token: \"ADD\", near: \"ADD campaignEligibility\"

I guess it does not accept the standard dynamodb update expression syntax, e.g. SET or ADD?

On the other hand, the update() function overwrites the entire String Set.

Your Environment

  • AWS Java SDK version used: 2.15.7
  • JDK version used: AdoptOpenJDK 11
  • Operating System and version: MacOS 10.15.7

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Comments:5 (3 by maintainers)

github_iconTop GitHub Comments

1reaction
helloworldlesscommented, Nov 19, 2020

I just noticed the same thing: there doesn’t seem to be a way to do an UpdateItem using a UpdateExpression with the Enhanced Client.

The Enhanced Client’s DynamoDbTable<T>#updateItem takes an UpdateItemEnhancedRequest which takes an item T, i.e. the full-blown entity.

It is supported on the AWS SDK v2 client however:

UpdateItemRequest updateItemRequest = UpdateItemRequest.builder()
                                           .updateExpression("...")
                                           .build();
dynamoDbClient.updateItem(updateItemRequest);

I think the main use case this does not cover is a TransactWriteItems with an UpdateItem using an UpdateExpression. To be fair, I don’t think this was supported in the v1 SDK either.

For now, I’m having to do a GetItem so that I can get the full blown entity and update a single attribute before adding it as an UpdateItem to the TransactWriteItems.

1reaction
debora-itocommented, Nov 5, 2020

@billydh I see you are using v1 DynamoDBLocal to run the code, my guess is this is not supported by DynamoDBLocal. I’ll run some tests with the DynamoDB API to be sure.

Read more comments on GitHub >

github_iconTop Results From Across the Web

UpdateItem - Amazon DynamoDB - AWS Documentation
Edits an existing item's attributes, or adds a new item to the table if it does not already exist. You can put, delete,...
Read more >
Why doesn't TableCell's method updateItem() have the correct ...
1 Answer 1 ... updateItem is called when TableView determines the cell value has changed. Since cells are reused, ... In this case...
Read more >
UpdateItemRequest (AWS SDK for Java - 2.18.34)
For UpdateItem , the valid values are: NONE - If ReturnValues is not specified, or if its value is NONE , then nothing...
Read more >
Cell (JavaFX 8) - Oracle Help Center
updateItem (T, boolean) method. If this is not done, the item and empty properties are not correctly set, and you are likely to...
Read more >
DynamoDB - dynamic method to insert or edit an item
It turned out that the problem was that ( due to the dynamic expression/attributes) I was telling dynamo to SET the values for...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found