Home > Mobile >  How to only update a single field in DynamoDB using AWS Go SDK
How to only update a single field in DynamoDB using AWS Go SDK

Time:12-15

hope someone can shed some light on this issue I've been trying to wrap my head around.

I've got this table in Dynamo, let's call it people and in this table, I've got the attributes of id as our partition key, name, lastName, and status. I'd like to be able to just update either a single attribute or all of them save for the ID. Now, this is how I've gone about it. I've created the following struct:

type PersonUpdate struct {
    FirstName     string `json:"firstName,omitempty"`
    LastName      string `json:"lastName,omitempty"`
    Status        string `json:"status,omitempty"`
}

And the request coming from the server would be to just update the person's last name, so our request body would look as follows:

{
    "lastName": "bob"
}

After we bind our request to our struct the object that would be sent to dynamo looks as as such:

{
    "firstName": "",
    "lastName": "bob",
    "status": "",
}

Now when it's time to write to dynamo as you can see, only one attribute should be updated while the rest which are empty/null should be ignored.

The code written to perform this action can be condensed to the following actions:

    // Marshal our object
    _, err := dynamodbattribute.MarshalMap(person)
    if err != nil{
        fmt.Println("some error marshaling")
    }
    // Create our update input
    input := &dynamodb.UpdateItemInput{
        key: map[string]*dynamodb.AttributeValue{
            "id":{
                S: aws.String("1234"),
            },
        },
        TableName: aws.String("people"),
        ExpressionAttributeValues: map[string]*dynamodb.AttributeValue{
            ":fn": {
                S: aws.String(person.FirstName),
            },
            ":ln":{
                S: aws.String(person.LastName),
            },
            ":st":{
                S: aws.String(person.Status),
            },
        },
        UpdateExpression: aws.String("set firstName = :fn, lastName = :ln, status = :st"),
        ReturnValues: aws.String("UPDATED_NEW"),
    }
    // Send update request to Dynamo
    _, err := service.UpdateItem(input)

Now, the update goes through with no issues, the problem is that the values for firstName and status which are null are getting passed as well. I've tried to go through their documentation but left a bit more confused. I know for a fact the Java SDK allows you to pass a flag called UPDATE_SKIP_NULL_ATTRIBUTES which allows you to skip those empty values and only update those which have data. I don't know what would be the equivalent in Go, any assistance/guidance would be great.

CodePudding user response:

In general, when updating items in DynamoDB, you have to specify the exact attributes you want to update, and their new values. If you don't want to update a certain attribute, you shouldn't include it in the update expression.

In your case, you can use the dynamodbattribute.MarshalMap function to marshal your PersonUpdate struct into a map of attribute values, but you can also use this function to selectively include only the attributes you want to update in the resulting map. For example, you could do something like this:

// Initialize the map
m := map[string]*dynamodb.AttributeValue{}

// Marshal only the attributes that have non-empty values
if person.FirstName != "" {
    m["firstName"], _ = dynamodbattribute.Marshal(person.FirstName)
}
if person.LastName != "" {
    m["lastName"], _ = dynamodbattribute.Marshal(person.LastName)
}
if person.Status != "" {
    m["status"], _ = dynamodbattribute.Marshal(person.Status)
}

// Create the update input
input := &dynamodb.UpdateItemInput{
    Key: map[string]*dynamodb.AttributeValue{
        "id":{
            S: aws.String("1234"),
        },
    },
    TableName: aws.String("people"),
    ExpressionAttributeValues: m,
    UpdateExpression: aws.String("set firstName = :fn, lastName = :ln, status = :st"),
    ReturnValues: aws.String("UPDATED_NEW"),
}

// Send update request to Dynamo
_, err := service.UpdateItem(input)

This way, only the attributes that have non-empty values in your PersonUpdate struct will be included in the update expression and in the ExpressionAttributeValues map. The attributes that are empty will be ignored.

I hope this helps! Let me know if you have any other questions.

CodePudding user response:

As @jarmod's comment says, apply the dynamodbav:",omitempty" tag to skip zero-value fields when marshaling the struct:

type PersonUpdate struct {
    FirstName     string `json:"firstName,omitempty" dynamodbav:",omitempty"`
    LastName      string `json:"lastName,omitempty" dynamodbav:",omitempty"`
    Status        string `json:"status,omitempty" dynamodbav:",omitempty"`
}
  • Related