Aws-sdk-go: DynamoDB: issue updating map attributes

Created on 17 Jul 2016  Â·  6Comments  Â·  Source: aws/aws-sdk-go

Hi,

When I'm updating a statistics.viewCount attribute like

        UpdateExpression: aws.String("SET #ATTR = #ATTR + :val"),
        ExpressionAttributeNames: map[string]*string{
            "#ATTR": aws.String("statistics.viewCount"),
        },

client throws an error: "Error #01: ValidationException: The provided expression refers to an attribute that does not exist in the item"

However, when I insert the value directly, like

UpdateExpression: aws.String("SET statistics.viewCount = statistics.viewCount + :val"),

this will work as expected.

Actually this mess will work too:

        UpdateExpression: aws.String("SET statistics.#ATTR = statistics.#ATTR + :val"),
        ExpressionAttributeNames: map[string]*string{
            "#ATTR": aws.String("viewCount"),
        },

Bug? Or I'm missing something?

Thank you!
Dennis

service-api

Most helpful comment

@mpmlj - Awesome job finding this! And no worries with taking our time, that's why we are here! If you have any more questions, please let us know.

All 6 comments

Hello @mpmlj, thank you for reaching out to us. This does look like a bug. I'll try to reproduce this and then see what is going on internally in the SDK/service.

Hello, @mpmlj - it looks like I cannot reproduce this error.

  put, err := svc.PutItem(&dynamodb.PutItemInput{                                                                          
    TableName: aws.String("TestTable"),                                                                                    
    Item: map[string]*dynamodb.AttributeValue{                                                                             
      "Test.Item": &dynamodb.AttributeValue{                                                                               
        N: aws.String("1"),                                                                                                
      },                                                                                                                   
      "TestKey": &dynamodb.AttributeValue{                                                                                 
        S: aws.String("Foo"),                                                                                              
      },                                                                                                                   
    },                                                                                                                     
  })                                                                                                                       
  fmt.Println(put, err)                                                                                                    

  out, err := svc.UpdateItem(&dynamodb.UpdateItemInput{                                                                    
    TableName: aws.String("TestTable"),                                                                                    
    Key: map[string]*dynamodb.AttributeValue{                                                                              
      "TestKey": &dynamodb.AttributeValue{                                                                                 
        S: aws.String("Foo"),                                                                                              
      },                                                                                                                   
    },                                                                                                                     
    UpdateExpression: aws.String("SET #ATTR = #ATTR + :val"),                                                              
    ExpressionAttributeValues: map[string]*dynamodb.AttributeValue{                                                        
      ":val": &dynamodb.AttributeValue{                                                                                    
        N: aws.String("1"),                                                                                                
      },                                                                                                                   
    },                                                                                                                     
    ExpressionAttributeNames: map[string]*string{                                                                          
      "#ATTR": aws.String("Test.Item"),                                                                                    
    },                                                                                                                     
  }) 

Does your code look something like this? In addition, can you provide the requestID? If you call the operation UpdateItemRequest and look at the request after calling Send, you'll be able to get the requestID.

@xibz
Alas, I can reproduce.

My code:

    params := &dynamodb.UpdateItemInput{
        TableName: aws.String("posts"),
        Key: map[string]*dynamodb.AttributeValue{
            "postId": {
                S: aws.String("1dbc76e1-04c0-42f6-aea8-2b82254ecea4"),
            },
        },
        UpdateExpression: aws.String("SET #ATTR = #ATTR + :val"),
        ExpressionAttributeValues: map[string]*dynamodb.AttributeValue{
            ":val": {
                N: aws.String("1"),
            },
        },
        ExpressionAttributeNames: map[string]*string{
            "#ATTR": aws.String("statistics.likeCount"),
        },
        ReturnValues: aws.String("NONE"),
    }

    _, err := DB.UpdateItem(params)
    if err != nil {
        return err
    }

Error message: _"Error #01: ValidationException: The provided expression refers to an attribute that does not exist in the item status code: 400, request id: ..."_

Btw., a little off-topic, why have you used AV as

":val": &dynamodb.AttributeValue{                                                                                    
        N: aws.String("1"),                                                                                                
      },

and not just

":val": {
                N: aws.String("1"),
            },

?

Thank you!
D

P.S.
I'm using AWS SDK Go 1.2.6, Go 1.6.2

@xibz

Update.
Just realized, I think I know the issue: in your example you are creating an attribute where "Test.Item" is a string, serving as a key name.
I.e. in Dynamo's console it looks like

Test.Item Number: 1

In my case, as per the ticket title, I have a map ("statistics"), i.e.

statistics Map {2}
   likeCount Number : 0

... and I'm trying to update values of its keys.

I.e., in my Post struct I have:

...
Statistics     PostStatistics `json:"statistics, omitempty"`
}

type PostStatistics struct {
    LikeCount    uint64 `json:"likeCount"`
        ...
}

Then I marshal this struct using dynamodbattribute.MarshalMap() with PutItem().

So my intent is to increment counters in the "statistics" map.

Ooookay....
Looks like i've found what's up: http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/ExpressionPlaceholders.html
"... what if you decided to use an expression attribute name instead? For example, _what would happen if you were to define #mmmk as a substitute for MyMap.MyKey_? DynamoDB would return an empty result, instead of the expected string. This is because _DynamoDB interprets a dot in an expression attribute value as a character within an attribute's name. When DynamoDB evaluates the expression attribute name #mmmk, it determines that MyMap.MyKey refers to a scalar attribute_ ...

The correct approach would be to define two expression attribute names, one for each element in the document path:

mm — MyMap

mk — MyKey "

Sorry for taking your time with this. Really not intuitive...

@mpmlj - Awesome job finding this! And no worries with taking our time, that's why we are here! If you have any more questions, please let us know.

Was this page helpful?
0 / 5 - 0 ratings